Каким образом можно решить проблему с созданием потока в динамической библиотеке, которая была загружена с использованием memory-module-sys?

У меня возникла проблема с созданием потока, в результате чего процесс завершается с кодом ошибки 0xc0000005 (STATUS_ACCESS_VIOLATION). Я собираю динамическую библиотеку в формате cdylib, использую последнюю стабильную версию Rust и библиотеку memory-module-sys. Я не могу разобраться с проблемой уже третий день, но не могу ее решить. 

В этом коде:

```rust
// main.rs
use std::ffi::CString;
use std::mem;
use std::thread::sleep;
use std::time::Duration;
use memory_module_sys::{MemoryGetProcAddress, MemoryLoadLibrary};

type FnTest = unsafe extern "C" fn();

fn main() {
	let bytes = include_bytes!("../../../target/debug/module.dll");
	let data = bytes.as_ptr() as *const std::ffi::c_void;
	let size = bytes.len() as libc::size_t;

	unsafe {
		let module = MemoryLoadLibrary(data, size);

		let name = CString::new("test").unwrap();
		let addr = MemoryGetProcAddress(module, name.as_ptr());
		let test: FnTest = mem::transmute(addr);

		test();
	}

	loop { sleep(Duration::from_millis(100)); }
}
```

и

```rust
// lib.rs
use std::thread;
use std::thread::sleep;
use std::time::Duration;

#[no_mangle]
pub unsafe extern "C" fn test() {
	println!("Hello from cdylib!");

	thread::spawn(|| {
		println!("Sleeping...");
		sleep(Duration::from_millis(500));
		println!("Done!");
	});
}
```

что именно может вызывать эту ошибку, и как я могу её исправить?
  • 11 марта 2025 г. 22:34
Ответы на вопрос 1
Ваш код создает поток в динамической библиотеке, но возникает ошибка доступа к памяти (STATUS_ACCESS_VIOLATION), потому что поток, запущенный в библиотеке, пытается использовать данные, которые могут быть недоступны или уничтожены после завершения вызова функции `test()`.

Вот несколько возможных причин этой ошибки и способы их устранения:

1. **Убедитесь, что библиотека загружена правильно**:
   Использование `memory-module-sys` подразумевает, что вы правильно загружаете вашу библиотеку. Убедитесь, что callback `MemoryLoadLibrary` возвращает правильное значение, и что вы проверяете, были ли ошибки при загрузке модуля.

2. **Долговечность данных**:
   Когда вы создаете новый поток с помощью `thread::spawn`, он может завершиться после выхода из функции, если у него нет устойчивых данных. В вашем случае, вы не храните хендл потока и не дожидаетесь его завершения. Это может привести к тому, что вызываемый вами код будет завершен, прежде чем поток закончит выполнение.

   Решение: сохранить хендл потока и использовать `thread::JoinHandle` чтобы дожидаться завершения потока, если это необходимо:

   ```rust
   #[no_mangle]
   pub unsafe extern "C" fn test() {
       println!("Hello from cdylib!");

       let handle = thread::spawn(|| {
           println!("Sleeping...");
           sleep(Duration::from_millis(500));
           println!("Done!");
       });

       // Дожидаемся завершения потока
       let _ = handle.join();
   }
   ```

3. **Отсутствие обработчиков ошибок**:
   Убедитесь, что вы добавили обработку ошибок, чтобы лучше понимать, где возникает проблема. Например, проверьте, что вы не получаете null-указатели или ошибки из `MemoryGetProcAddress`.

4. **Синхронизация и состояние потоков**:
   Убедитесь, что память, проверяемая потоком, корректна и актуальна, особенно для данных, которые могут быть изменены во время выполнения. Используйте `Arc` и `Mutex`, чтобы передавать данные между потоками, если это необходимо.

5. **Возвращение к главному потоку**:
   Если ваш главный поток завершится до завершения работы вашего потока, может произойти доступ к уже уничтоженной памяти. Убедитесь, что главный поток ожидает завершения всех дочерних потоков.

6. **Использование статических функций**:
   В некоторых случаях проблемы могут возникать из-за того факта, что функции и переменные не имеют фиксированной области видимости или времени жизни. Рассмотрите возможность использования статических переменных или других подходов для хранения данных, которые нужно использовать в потоке.

Попробуйте внести эти изменения и посмотреть, разрешит ли это вашу проблему. Если ошибка сохраняется, было бы полезно предоставить больше информации о том, как именно происходит ошибка (например трассировка стека, если она доступна).
Похожие вопросы