你怎么能在鐵銹里做一個安全的靜電單例?這是一個有爭議的話題,所以讓我先解釋一下我的用例,然后談談實際的問題。我發現對于一堆不安全的事情,確保不泄漏內存是很重要的;如果您開始使用transmute()和forget()..例如,將一個盒裝實例傳遞給C代碼,時間長短任意,然后取出它,然后使用transmute.假設我有一個用于這種API的安全包裝器:trait Foo {}struct CBox;impl CBox {
/// Stores value in a bound C api, forget(value)
fn set<T: Foo>(value: T) {
// ...
}
/// Periodically call this and maybe get a callback invoked
fn poll(_: Box<Fn<(EventType, Foo), ()> + Send>) {
// ...
}}impl Drop for CBox {
fn drop(&mut self) {
// Safely load all saved Foo's here and discard them, preventing memory leaks
}}要測試這是其實沒有泄漏任何記憶,我需要這樣的測試:#[cfg(test)]mod test {
struct IsFoo;
impl Foo for IsFoo {}
impl Drop for IsFoo {
fn drop(&mut self) {
Static::touch();
}
}
#[test]
fn test_drops_actually_work() {
guard = Static::lock(); // Prevent any other use of Static concurrently
Static::reset(); // Set to zero
{
let c = CBox;
c.set(IsFoo);
c.set(IsFoo);
c.poll(/*...*/);
}
assert!(Static::get() == 2); // Assert that all expected drops were invoked
guard.release();
}}如何創建這種類型的靜態單例對象?它必須使用Semaphore樣式保護鎖,以確保多個測試不并發運行,然后不安全地訪問某種靜態可變值。我想也許這樣的實施是可行的,但實際上,它失敗了,因為有時爭用條件會導致重復執行init:/// Global instancestatic mut INSTANCE_LOCK: bool = false;static mut INSTANCE: *mut StaticUtils = 0 as
*mut StaticUtils;static mut WRITE_LOCK: *mut Semaphore = 0 as *mut Semaphore;static mut LOCK: *mut Semaphore = 0 as *mut Semaphore;
/// Generate instances if they don't existunsafe fn init() {
if !INSTANCE_LOCK {
INSTANCE_LOCK = true;
INSTANCE = transmute(box StaticUtils::new());
WRITE_LOCK = transmute(box Semaphore::new(1));
LOCK = transmute(box Semaphore::new(1));
}}特別要注意的是,不同于普通程序,您可以確定入口點(Main)總是在單個任務中運行,RUST中的測試運行程序不提供任何這樣的單一入口點。顯然,除了指定任務的最大數量之外,如果有幾十個測試,那么只需要做幾個這樣的事情,并且只在這一種情況下將測試任務池限制為一個是緩慢和毫無意義的。
你怎么能在鐵銹里做一個安全的靜電單例?
慕蓋茨4494581
2019-07-12 15:28:31
