Logo Search packages:      
Sourcecode: tbb version File versions  Download package

bool tbb::queuing_rw_mutex::scoped_lock::upgrade_to_writer (  ) 

Upgrade reader to become a writer.

A method to upgrade a reader to a writer.

Returns true if the upgrade happened without re-acquiring the lock and false if opposite

See more detailed comments in the SPIN model: <TBB directory>="">/tools/spin_models/ReaderWriterMutex.pml

Definition at line 377 of file queuing_rw_mutex.cpp.

References tbb::acquire, acquire_internal_lock(), tbb::FLAG, going, tbb::internal::ExponentialBackoff::pause(), prev, tbb::release, release_internal_lock(), state, unblock_or_wait_on_internal_lock(), and wait_for_release_of_internal_lock().

{
    __TBB_ASSERT( state==STATE_ACTIVEREADER, "only active reader can be upgraded" );

    queuing_rw_mutex::scoped_lock * tmp;
    queuing_rw_mutex::scoped_lock * me = this;

    ITT_NOTIFY(sync_releasing, mutex);
    state = STATE_UPGRADE_REQUESTED;
requested:
    __TBB_ASSERT( !( tricky_pointer(next) & FLAG ), "use of corrupted pointer!" );
    acquire_internal_lock();
    if( this != mutex->q_tail.compare_and_swap<tbb::release>(tricky_pointer(me)|FLAG, this) ) {
        SpinwaitWhileEq( next, (void*)NULL );
        queuing_rw_mutex::scoped_lock * n;
        n = tricky_pointer::fetch_and_add<tbb::acquire>(&(this->next), FLAG);
        unsigned short n_state = n->state;
        /* the next reader can be blocked by our state. the best thing to do is to unblock it */
        if( n_state & STATE_COMBINED_WAITINGREADER )
            __TBB_store_with_release(n->going,1);
        tmp = tricky_pointer::fetch_and_store<tbb::release>(&(n->prev), this);
        unblock_or_wait_on_internal_lock(get_flag(tmp));
        if( n_state & (STATE_COMBINED_READER | STATE_UPGRADE_REQUESTED) ) {
            // save n|FLAG for simplicity of following comparisons
            tmp = tricky_pointer(n)|FLAG;
            ExponentialBackoff backoff;
            while(next==tmp) {
                if( state & STATE_COMBINED_UPGRADING ) {
                    if( __TBB_load_with_acquire(next)==tmp )
                        next = n;
                    goto waiting;
                }
                backoff.pause();
            }
            __TBB_ASSERT(next!=(tricky_pointer(n)|FLAG), NULL);
            goto requested;
        } else {
            __TBB_ASSERT( n_state & (STATE_WRITER | STATE_UPGRADE_WAITING), "unexpected state");
            __TBB_ASSERT( (tricky_pointer(n)|FLAG)==next, NULL);
            next = n;
        }
    } else {
        /* We are in the tail; whoever comes next is blocked by q_tail&FLAG */
        release_internal_lock();
    } // if( this != mutex->q_tail... )
    state.compare_and_swap<tbb::acquire>(STATE_UPGRADE_WAITING, STATE_UPGRADE_REQUESTED);

waiting:
    __TBB_ASSERT( !( tricky_pointer(next) & FLAG ), "use of corrupted pointer!" );
    __TBB_ASSERT( state & STATE_COMBINED_UPGRADING, "wrong state at upgrade waiting_retry" );
    __TBB_ASSERT( me==this, NULL );
    ITT_NOTIFY(sync_prepare, mutex);
    /* if noone was blocked by the "corrupted" q_tail, turn it back */
    mutex->q_tail.compare_and_swap<tbb::release>( this, tricky_pointer(me)|FLAG );
    queuing_rw_mutex::scoped_lock * pred;
    pred = tricky_pointer::fetch_and_add<tbb::acquire>(&(this->prev), FLAG);
    if( pred ) {
        bool success = pred->try_acquire_internal_lock();
        //unsigned short pred_state = // keep the variable in code just in case
            pred->state.compare_and_swap<tbb::release>(STATE_UPGRADE_WAITING, STATE_UPGRADE_REQUESTED);
        if( !success ) {
            tmp = tricky_pointer::compare_and_swap<tbb::release>(&(this->prev), pred, tricky_pointer(pred)|FLAG );
            if( tricky_pointer(tmp)&FLAG ) {
                SpinwaitWhileEq(this->prev, pred);
                pred = this->prev;
            } else {
                SpinwaitWhileEq( this->prev, tricky_pointer(pred)|FLAG );
                pred->release_internal_lock();
            }
        } else {
            this->prev = pred;
            pred->release_internal_lock();
            SpinwaitWhileEq(this->prev, pred);
            pred = this->prev;
        }
        if( pred )
            goto waiting;
    } else {
        // restore the corrupted prev field for possible further use (e.g. if downgrade back to reader)
        this->prev = pred;
    }
    __TBB_ASSERT( !pred && !this->prev, "" );

    // additional lifetime issue prevention checks
    // wait for the successor to finish working with my fields
    wait_for_release_of_internal_lock();
    // now wait for the predecesor to finish working with my fields
    SpinwaitWhileEq( going, 2 );
    // there is an acquire semantics statement in the end of SpinwaitWhileEq.

    bool result = ( state != STATE_UPGRADE_LOSER );
    state = STATE_WRITER;
    going = 1;

    ITT_NOTIFY(sync_acquired, mutex);
    return result;
}


Generated by  Doxygen 1.6.0   Back to index