2017-05-31 5 views
0

私が意図したように、コードのこの部分が動作しないことが判明:私はcv.wait_untilコールで1秒間待ってから繰り返すコードを期待条件変数と#pragmaパックのバグ

#pragma pack(push, 1) 

class myclass { 
protected: 
    bool mycrasher[1]; // with mycrasher[4] it works! 
    std::mutex mtx; 
    std::condition_variable cv; 

    void thread_func() { 
     std::this_thread::sleep_for(std::chrono::seconds(1)); 

     std::chrono::milliseconds timeout(1000); 
     std::unique_lock<std::mutex> l(mtx, std::defer_lock); 
     while (true) { 
      auto now = std::chrono::system_clock::now(); 
      l.lock(); 
      while (true) { 
       std::cout << "Waiting..." << std::endl; 
       auto result = cv.wait_until(l, now + timeout); 
       std::cout << "Timed out..." << std::endl; 
       if (result == std::cv_status::timeout) 
        break; 
      } 
      l.unlock(); 
     } 
    } 

public: 
    myclass() { 
     std::lock_guard<std::mutex> l(mtx); 
     std::thread *t = new std::thread(&myclass::thread_func, this); 
     t->detach(); 
    }; 

    void start() { 
     std::cout << "myclass started." << std::endl; 
     std::cout << "sizeof(std::mutex) = " << sizeof(std::mutex) << std::endl; 
     std::cout << "sizeof(std::condition_variable) = " << sizeof(std::condition_variable) << std::endl; 
    } 
}; 
#pragma pack(pop) 

int main() { 
    myclass x; 
    x.start(); 
    std::this_thread::sleep_for(std::chrono::seconds(60)); 
} 

が、単にコールにハングアップします。私がmutexとCVをパックしているので、#pragmaディレクティブを削除すると、(直感的に)問題がなくなります。 pragmaの有無にかかわらず

myclass started. 
sizeof(std::mutex) = 40 
sizeof(std::condition_variable) = 48 

をので、梱包は本当の問題ではないようだ。私はこのコードを実行するときしかし、私が取得します。

さらに、mycrasher変数を4バイト境界に揃えると、問題が消えてしまうことも発見しました。同様に、std::condition_variable cv宣言の後に変数を移動すると、問題は消えますが、std::mutex mtxstd::condition_variable cvの間で移動すると、それでも問題は解決しません。

なぜCVが正しく整列されていないときにcv.wait_untilコールでスニペットがハングするのですか?パフォーマンスヒットが予想されますが、プレーストールではありません。

Debian 8システムでg ++ 4.9.2およびg ++ 6.3で再現されました。

+1

'#pragma pack'はあなたの構造体にのみ使用され、あなたの構造体で使用するものはありません。奇数アドレスにmutexや条件変数を設定することはできないようです。おそらく、あなたがなぜ* #pragma packを使用したいのか、なぜそれを使って解決しようとしているのか、私たちはあなたにその問題を手伝うことができますか? –

+0

@Someprogrammerdudeありがとう、私はそれを発見しました。私は*なぜそれが働くのを理解したかったのですか?これはメモリを大量に消費するアプリケーションの一部でなければならないため、パックされたデータはすべてのバイトを圧縮することを目的としています。 – xmas79

+0

@ xmas79:それは逆行する可能性があります。すべてのバイトを圧縮すると、無関係なデータを同じキャッシュラインに置くことができます。これは早すぎる最適化の別のケースのように見えます。 – MSalters

答えて

1

パッキングを使用しないでください。ミューテックスと条件変数を持つ構造体を使用しないでください。あなたが本当に必要と思っているのであれば、これらのオブジェクトの多くを作成すべきではないので、あなたのアプローチには何か問題があります。

は、単純な回避策として、置くあなたのごmtxcv以下mycrasher

#pragma pack(push, 1) 
class myclass 
{ 
protected: 
    std::mutex mtx; 
    std::condition_variable cv; 
    bool mycrasher[1]; 

    void thread_func(); 
public: 
    myclass(); 
    void start(); 
}; 
#pragma pack(pop) 

有効に梱包して予想通り、それは最も可能性の高い動作しないです理由ですのでmtxcv奇数アドレスに終わると、これらを使用する内部コードには異なる期待があります。単純にクラッシュするかもしれないいくつかのプラットフォーム(例えばARM)で。

補足として、コードには整列の問題だけでなく、myclassコンストラクタ内のヒープ上のスレッドオブジェクトもリークされます。

+0

ありがとう、これは私がすでに発見したものです。私はmutexやcvのドキュメントで明白な期待を見つけることができません。だから、なぜ私はミューテックスと条件変数を持つ構造**でそれをしてはいけないのですか? – xmas79

+0

@ xmas79パッキングを要求するために多くを作成するのは正常ではないためです。これらは数千または数万で作成すべきではありません。予期しないアライメントのためにデータが破損する場所を見つけるには、デバッガでコードをステップ実行する必要があります。また、最適化を行わずにプログラムをビルドしようとすると、問題が依然として存在する場合、内部的なpthread関数はそのデータが適切に整列されることを期待します。 – Pavel

+0

それは私のせいではなく、図書館のバグですか? – xmas79