2

Linux から Solaris にプログラムを移植し、solarisstudio 12.3 でビルドします。

次の定義があります。

typedef std::list<ISocketMultiplexerJob *> CSocketJobs;
typedef CSocketJobs::iterator CJobCursor;

CSocketJobs   m_socketJobs;

そしてこのコード:

CSocketMultiplexer::CJobCursor
CSocketMultiplexer::nextCursor(CJobCursor cursor)
{
    CLock lock(m_mutex);

        CJobCursor j = m_socketJobs.end();
        CJobCursor i = cursor;
        while (++i != m_socketJobs.end()) {
            if (*i != m_cursorMark) {   // CRASHES HERE!!
                j = i;

                // move our cursor just past the job
                m_socketJobs.splice(++i, m_socketJobs, cursor);
                break;                                                  
             }
        }
        return j;
  }

次の理由により、上記の行でクラッシュします。

(dbx) print i
i = {
    _C_node = (nil)
}

「++i」反復がリストから外れたように見えますが、m_socketJobs.end() に対するテストはそれを確認できず、通過させました。デバッグのヘルプが必要です。たとえば、dbx の *i をより C++ に適した方法で解析するにはどうすればよいですか?

nextCursor() の呼び出し元は、このスニペット <> にあります。

                // collect poll entries
                if (m_update) {
                       m_update = false;
                       pfds.clear();
                       pfds.reserve(m_socketJobMap.size());

                       CJobCursor cursor    = newCursor();
                       CJobCursor jobCursor = nextCursor(cursor);
                       while (jobCursor != m_socketJobs.end()) {
                               ISocketMultiplexerJob* job = *jobCursor;
                               if (job != NULL) {
                                    pfd.m_socket = job->getSocket();
                                    pfd.m_events = 0;
                                    if (job->isReadable()) {
                                            pfd.m_events |= IArchNetwork::kPOLLIN;
                                    }
                                    if (job->isWritable()) {
                                            pfd.m_events |= IArchNetwork::kPOLLOUT;
                                    }
                                    pfds.push_back(pfd);
                               }                               
                               jobCursor = nextCursor(cursor);  //FATAL CALL
                       }

関数 newCursor() は次のとおりです。

CSocketMultiplexer::CJobCursor
   CSocketMultiplexer::newCursor() {  
     CLock lock(m_mutex);
     return m_socketJobs.insert(m_socketJobs.begin(), m_cursorMark);
}

私はいくつかの変更を行い、 newCursor()/nextCursor() が機能し、機能しないことを発見しました....おそらく別のスレッドがコンテキストを傷つけています。以下の例 (私のプログラムに埋め込まれています) では、「CJobCursor c = newCursor();」の最初の init 「c= nextCursor(c);」という行を挿入できます。私のプログラムのどこにいてもクラッシュしません。しかし、「BAD」というコメントが付いた次のものには欠陥があり、2 回目の nexCursor() 呼び出しでクラッシュします。これは興味深いと思いますが、まだ説明がありません。コンテキストが物事を殺しているので、プログラム全体の中でテストを続ける必要があると思います。どう思いますか?

void
CSocketMultiplexer::serviceThread(void*)
{
    std::vector<IArchNetwork::CPollEntry> pfds;
    IArchNetwork::CPollEntry pfd;

                    CJobCursor c    = newCursor();
                    CJobCursor j = nextCursor(c);
                    c = nextCursor(c);
                    c = nextCursor(c);

    // service the connections
    for (;;) {
            CThread::testCancel();

            // wait until there are jobs to handle
            {
                    CLock lock(m_mutex);
                    while (!(bool)*m_jobsReady) {
                            m_jobsReady->wait();
                    }
            }

            // lock the job list
            lockJobListLock();
            lockJobList();

            // collect poll entries
            if (m_update) {
                    m_update = false;
                    pfds.clear();
                    pfds.reserve(m_socketJobMap.size());

                    CJobCursor cursor    = newCursor();  //BAD, Ill-fated object
                    CJobCursor jobCursor = nextCursor(cursor);
                    c = nextCursor(c);
                    cursor = nextCursor(cursor);        // SEGV's here
                    while (jobCursor != m_socketJobs.end()) {
                            ISocketMultiplexerJob* job = *jobCursor;
                            if (job != NULL) {
                                    pfd.m_socket = job->getSocket();
                                    pfd.m_events = 0;
                                    if (job->isReadable()) {
                                            pfd.m_events |= IArchNetwork::kPOLLIN;
                                    }
                                    if (job->isWritable()) {
                                            pfd.m_events |= IArchNetwork::kPOLLOUT;
                                    }
                                    pfds.push_back(pfd);
                            }                               
                            c = nextCursor(c);
                            jobCursor = nextCursor(cursor);
                    }
                    c = nextCursor(c);
                    deleteCursor(cursor);
            }
4

1 に答える 1

1

「++i」の繰り返しがリストから外れたようです

いいえ、最後のイテレータではなく、無効なイテレータ(おそらくデフォルトで構築されたもの)のように見えると思います。

Astd::list<T>::iterator はリストを逆方向にデクリメントできる必要があるため、null を指すことはできません。そうしないと、最後に到達して null になると、再び逆方向に進むことができなくなります。通常、past-the-endstd::listイテレーターはセンチネル ノードを指す必要があります。

したがって、何回インクリメントしても到達できないことを意味するi要素を指していないため、常に真になります。m_socketJobsm_socketJobs.end()ii != m_socketJobs.end()

関数の先頭に追加assert( i != CJobCursor() )してみてください。無効なイテレータで関数を呼び出したと思われるため、そこで中止されるに違いありません。

(++i != m_socketJobs.end())が true であるという理由だけで、反復子が逆参照可能であると想定しないでください。

于 2014-12-18T00:58:04.853 に答える