diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f2f89f..9b9bb7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 0.5.1 (March 27, 2026) + +- Add `InjectorPP::new_global()` constructor for cross-thread fake visibility. + - Fakes registered through `new_global()` use direct code patching (0.4.0-style) and are visible to **all threads**, including background threads, timers, and thread pools like rayon. + - `new()` remains the default with thread-local dispatch for parallel test execution. + - `new_global()` acquires an exclusive lock, serializing tests that use it. +- Fix fakes not visible from rayon worker threads and background timer threads (issue #121). +- Add IAT thunk resolution to PatchGuard path on Windows x86_64. + # 0.5.0 (March 15, 2026) - Add macOS support. diff --git a/Cargo.toml b/Cargo.toml index f5c69bb..161fb4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "injectorpp" -version = "0.5.0" +version = "0.5.1" authors = ["Jingyu Ma "] license = "MIT" readme = "README.md" diff --git a/README.md b/README.md index c892413..5618acc 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Add `injectorpp` to the `Cargo.toml`: ```toml [dev-dependencies] -injectorpp = "0.5.0" +injectorpp = "0.5.1" ``` Below `profile.test` config is recommended to make sure `injectorpp` working correctly in tests. If you have workspace, make sure add this on the top level of `Cargo.toml`: @@ -88,6 +88,27 @@ use injectorpp::interface::injector::*; Below are multiple ways to config the function behavior. +## Thread-local vs Global mode + +By default, `InjectorPP::new()` uses **thread-local dispatch** — fakes are only visible on the thread that created the injector. This enables parallel test execution without interference between tests. + +However, if your code under test spawns background threads, timers, or uses thread pools, those worker threads won't see thread-local fakes. For these cases, use `InjectorPP::new_global()`: + +```rust +// Thread-local mode (default) — fakes only visible on the current thread +let mut injector = InjectorPP::new(); + +// Global mode — fakes visible to ALL threads (background threads, timers, thread pools) +let mut injector = InjectorPP::new_global(); +``` + +`new_global()` uses direct code patching, making fakes visible process-wide. It acquires an exclusive lock, so tests using `new_global()` run serialized to prevent interference. + +**When to use `new_global()`:** +- Your faked function is called from a background thread or timer +- You use thread pool APIs that execute work on worker threads +- You need the same behavior as injectorpp 0.4.0 + ## `will_return_boolean` If the function only returns boolean and you only want to make it constantly returns a specific boolean value, you can use `will_return_boolean`: diff --git a/injectorpp-macros/Cargo.toml b/injectorpp-macros/Cargo.toml index 0caaa33..5ec6ac2 100644 --- a/injectorpp-macros/Cargo.toml +++ b/injectorpp-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "injectorpp-macros" -version = "0.5.0" +version = "0.5.1" authors = ["Jingyu Ma "] license = "MIT" repository = "https://github.com/microsoft/injectorppforrust" diff --git a/tests/global.rs b/tests/global.rs index 0d1adba..efe9911 100644 --- a/tests/global.rs +++ b/tests/global.rs @@ -186,7 +186,7 @@ fn test_global_fake_unchecked_cross_thread() { } /// Verifies that `new()` (thread-local mode) still works correctly — fakes are NOT visible -/// from spawned threads (default 0.5.0 behavior). +/// from spawned threads (default thread-local behavior). #[test] #[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "arm"))] fn test_thread_local_mode_not_visible_from_spawned_thread() {