2016-12-25 8 views
3

大きなObjタイプのグローバルオブジェクトプールを実装しようとしています。ここでPOOLのためのコードは次のとおりです。ここでなぜMutexのロックを解除しないのですか?

static mut POOL: Option<Mutex<Vec<Obj>>> = None; 
static INIT: Once = ONCE_INIT; 

pub struct Obj; 

は、私がアクセスしPOOLをロックしています方法です:

fn get_pool<'a>() -> MutexGuard<'a, Vec<Obj>> { 
    unsafe { 
     match POOL { 
      Some(ref mutex) => mutex.lock().unwrap(), 
      None   => { 
       INIT.call_once(|| { 
        POOL = Some(Mutex::new(vec![])); 
       }); 
       get_pool() 
      } 
     } 
    } 
} 

これが問題を引き起こしているコードです:

impl Drop for Obj { 
    fn drop(&mut self) { 
     println!("dropping."); 
     println!("hangs here..."); 
     get_pool().push(Obj {}); 
    } 
} 

impl Obj { 
    pub fn new() -> Obj { 
     println!("initializing"); 
     get_pool().pop().unwrap_or(Obj {}) 
     // for some reason, the mutex does not get unlocked at this point... 
    } 
} 

私はそれを考えます戻り値get_poolMutexGuardの寿命'aと関係があります。率直に言って、これらの生涯パラメータの仕組みについては、おそらくちょっと混乱しています。

ここには、実施例のあるlink to a playgroundがあります。あなたの助けとメリークリスマスをありがとう。問題は、このラインに位置する

答えて

4

get_pool().pop().unwrap_or(Obj {}) 

あなたがget_pool()を呼んでいるので、あなたは、ミューテックスをロックし、それが行の最後までロックが解除されることはありません。ただし、unwrap_or()への呼び出しでは、新しいObjを作成します。 vecにオブジェクトがあった場合は使用されません。後で作成されるため、mutexが解放される前に削除されます。ドロップがミューテックスをロックしようとすると、デッドロックが発生します。あなたは危険なコードを避けるためにlazy-staticを使用することができ、関連する注意点としては

let o = get_pool().pop(); 
o.unwrap_or(Obj {}) 

これを修正するには、2行にその文を分割

#![feature(drop_types_in_const)] 
use std::sync::{Mutex, MutexGuard}; 

#[macro_use] 
extern crate lazy_static; 

lazy_static! { 
    static ref POOL: Mutex<Vec<Obj>> = Mutex::new(vec![]); 
} 

pub struct Obj; 

fn get_pool<'a>() -> MutexGuard<'a, Vec<Obj>> { 
     POOL.lock().unwrap() 
} 

impl Drop for Obj { 
    fn drop(&mut self) { 
     println!("dropping."); 
     println!("hangs here..."); 
     get_pool().push(Obj {}); 
     println!("not here..."); 
    } 
} 

impl Obj { 
    pub fn new() -> Obj { 
     println!("initializing"); 
     let o = get_pool().pop(); 
     o.unwrap_or(Obj {}) 
    } 
} 

fn main() { 
    Obj::new(); 
    Obj::new(); 
    println!("Now reaches this point."); 
} 

編集を

要望どおり、私はこれをどのように診断したか説明します。

  1. 最初に、サンプルを使用して問題を再現できるかどうかを確認しました。私はそれが可能で、コードは単純で十分にクリアでした。これはすべて問題ありませんでした。ブロックprintln!("not here...");を追加するだけで、ブロックの最後ではなく、上記のステートメントで100%確実にハングアップする必要がありました。
  2. 最初のスキャンでは、問題が発生するためにObj::new();を2回呼び出す必要があることに気付きました。したがって、次の目標は両方の呼び出しの違いを見つけることです。 (私の錆の知識はまだコードを読むだけでこのエラーを発見するには十分ではありません)。
  3. 最初の呼び出しでPOOLが初期化されていないため、私はメイン(unsafe{INIT.call_once(||{POOL=Some(Mutex::new(vec![]));});})の先頭に初期化を追加しましたが、何も変更されませんでした。
  4. Objが削除されたときにオブジェクトがプールに追加されるため、メイン(get_pool().push(Obj {});)の先頭にオブジェクトを追加しました。今度は最初のObj::new();にハングアップします。
  5. 私はget_pool().pop().unwrap_or(Obj {});をメインに呼び出すことで、さらに簡略化できました。
  6. ここで、その行を部分的に削除または分割して、どこがハングするかを正確に判断することができました。そうすることで、私はそれを修正することができたのを見た。その後、私は余分なObjが作成されたことを実現しました。なお、錆の範囲は現在lexicalです。
  7. get_pool()を含む行をdrop()に削除して、何度も何度も呼び出された場合は、drop()が何回呼び出されたかを数えてみました。私は2回ではなく3回と呼ばれていたことを認識していませんでした。

一般的な注意点として、この質問のタイトルは「なぜMutexをロック解除しないのですか」です。これは、コンパイラのバグ、または標準ライブラリのバグとして解釈される可能性があります。ほとんどの場合(> 99%)、そうではありません。それを念頭に置き、間違った問題に焦点を当てることは重要です。

この問題はグローバル共有状態に関連しています。それを避けてください。 (はい、私はそれが常に可能ではないことを知っています)。

+1

ありがとうございました!それは、このバージョンとそれがベースにしている複雑なリアルバージョンの両方で私の問題を解決しました。あなたがそれを理解するために使用したプロセスを説明しようとする可能性はありますか? – ethanabrooks

+0

私はあなたの単純化された例がなければこれを解決できませんでした!さて、私は明日のプロセスについていくつかの詳細を追加します。 – wimh

関連する問題