Rogue Wave banner
Previous fileTop of DocumentContentsIndexNext file

23.2 The Locking Mechanism

The locking mechanism has been designed for maximum efficiency, with no reentrant locks needed. There are two different mutex objects involved in this scheme:

It is easy to see that locking and unlocking the buffer after each independent buffer operation would be disastrous. For example, when inserting a char* sequence of characters, a call to an inline basic_streambuf function is made for each character inserted; therefore, the locking mechanism is carried out at a higher level. For all formatted and unformatted stream functions, the locking is performed in the sentry object constructor, and the release in the sentry object destructor. If the function does not make use of the sentry class, the lock is directly performed inside the function. This is the case with basic_istream::seekg and basic_ostream::seekg.

Consider the following example:

Thread 1:
Thread 2:
cout << "Hello Thread 1" << endl;
cout << "Hello Thread 2" << endl;

If Thread 1 is the first thread locking the buffer, the sequence of characters "Hello Thread 1" is output to stdout and the lock is released; Thread 2 then acquires the lock, outputs its sequence of characters, and releases it.

23.2.1 Protecting the Buffer

Notice that only one lock occurs on the basic_streambuf mutex object for each stream operation. The advantage of this scheme is obviously high performance, but the drawback is that while buffer functionality is directly accessed, the buffer is left unprotected. We therefore provide functions and manipulators to solve this problem. The following example explains how they work:

Thread 1:
Thread 2:
cout << "Hello Thread 1" << endl;
cout<< __lock;
do {
cout.rdbuf()->sputc(*t);
} while ( *t++!=0 )
cout << __unlock;

In this scheme, if Thread 2 is the first one to execute, when it gets to the statement cout<< __lock;, it locks the basic_streambuf object pointed at by cout.rdbuf(). Thread 1 cannot output its sequence of characters until Thread 2 reaches the statement cout << __unlock;, which releases the lock. This technique is easy to use and allows high performance for both stream and buffer operations.

23.2.2 Locking Several Stream Operations

There is also a way to lock several stream operations within one thread in order to preserve the global order of operations carried out by several threads running concurrently. The following example illustrates this technique:

Thread 1:
Thread 2:
cout << __lock;
cout << "Thread 1 begin" << endl;
cout << "Thread 1 completion" << endl;
cout << __unlock;
cout << "Thread 2 begin" << endl;

In this example, if Thread 1 is the first thread locking the basic_streambuf object attached to cout, Thread 2 cannot output its sequence of characters until Thread 1 reaches the statement cout << __unlock;. The result is to preserve the order of the output, which is:

Thread 1 locking first:
Thread 1 begin
Thread 1 completion
Thread 2 begin
Thread 2 locking first:
Thread 2 begin
Thread 1 begin
Thread 1 completion


Previous fileTop of DocumentContentsIndexNext file

OEM Edition, ©Copyright 1999, Rogue Wave Software, Inc.
Contact Rogue Wave about documentation or support issues.