Sun Studio 12: C++ User's Guide

11.4.1 Organization of the MT-Safe iostream Library

The organization of the MT-safe iostream library is slightly different from other versions of the iostream library. The exported interface of the library refers to the public and protected member functions of the iostream classes and the set of base classes available, and is consistent with other versions; however, the class hierarchy is different. See 11.4.2 Interface Changes to the iostream Library for details.

The original core classes have been renamed with the prefix unsafe_. Table 11–1 lists the classes that are the core of the iostream package.

Table 11–1 iostream Original Core Classes

Class 

Description 

stream_MT

The base class for MT-safe classes. 

streambuf

The base class for buffers. 

unsafe_ios

A class that contains state variables that are common to the various stream classes; for example, error and formatting state. 

unsafe_istream

A class that supports formatted and unformatted conversion from sequences of characters retrieved from the streambufs.

unsafe_ostream

A class that supports formatted and unformatted conversion to sequences of characters stored into the streambufs.

unsafe_iostream

A class that combines unsafe_istream and unsafe_ostream classes for bidirectional operations.

Each MT-safe class is derived from the base class stream_MT. Each MT-safe class, except streambuf, is also derived from the existing unsafe_ base class. Here are some examples:


class streambuf: public stream_MT {...};
class ios: virtual public unsafe_ios, public stream_MT {...};
class istream: virtual public ios, public unsafe_istream {...};

The class stream_MT provides the mutual exclusion (mutex) locks required to make each iostream class MT-safe; it also provides a facility that dynamically enables and disables the locks so that the MT-safe property can be dynamically changed. The basic functionality for I/O conversion and buffer management are organized into the unsafe_ classes; the MT-safe additions to the library are confined to the derived classes. The MT-safe version of each class contains the same protected and public member functions as the unsafe_ base class. Each member function in the MT-safe version class acts as a wrapper that locks the object, calls the same function in the unsafe_ base class, and unlocks the object.


Note –

The class streambuf is not derived from an unsafe class. The public and protected member functions of class streambuf are reentrant by locking. Unlocked versions, suffixed with _unlocked, are also provided.


11.4.1.1 Public Conversion Routines

A set of reentrant public functions that are MT-safe have been added to the iostream interface. A user-specified buffer is an additional argument to each function. These functions are described as follows.

Table 11–2 MT-Safe Reentrant Public Functions

Function 

Description  

char *oct_r (char *buf,

int buflen,

long num,

int width)

Returns a pointer to the ASCII string that represents the number in octal. A width of nonzero is assumed to be the field width for formatting. The returned value is not guaranteed to point to the beginning of the user-provided buffer. 

char *hex_r (char *buf,

int buflen,

long num,

int width)

Returns a pointer to the ASCII string that represents the number in hexadecimal. A width of nonzero is assumed to be the field width for formatting. The returned value is not guaranteed to point to the beginning of the user-provided buffer. 

char *dec_r (char *buf,

int buflen,

long num,

int width)

Returns a pointer to the ASCII string that represents the number in decimal. A width of nonzero is assumed to be the field width for formatting. The returned value is not guaranteed to point to the beginning of the user-provided buffer. 

char *chr_r (char *buf,

int buflen,

long num,

int width)

Returns a pointer to the ASCII string that contains character chr. If the width is nonzero, the string contains width blanks followed by chr. The returned value is not guaranteed to point to the beginning of the user-provided buffer.

char *form_r (char *buf,

int buflen,

long num,

int width)

Returns a pointer of the string formatted by sprintf, using the format string format and any remaining arguments. The buffer must have sufficient space to contain the formatted string.


Note –

The public conversion routines of the iostream library ( oct, hex, dec, chr, and form) that are present to ensure compatibility with an earlier version of libC are not MT-safe.


11.4.1.2 Compiling and Linking With the MT-Safe libC Library

When you build an application that uses the iostream classes of the libC library to run in a multithreaded environment, compile and link the source code of the application using the -mt option. This option passes -D_REENTRANT to the preprocessor and -lthread to the linker.


Note –

Use -mt (rather than -lthread) to link with libC and libthread. This option ensures proper linking order of the libraries. Using -lthread improperly could cause your application to work incorrectly.


Single-threaded applications that use iostream classes do not require special compiler or linker options. By default, the compiler links with the libC library.

11.4.1.3 MT-Safe iostream Restrictions

The restricted definition of MT-safety for the iostream library means that a number of programming idioms used with iostream are unsafe in a multithreaded environment using shared iostream objects.

Checking Error State

To be MT-safe, error checking must occur in a critical region with the I/O operation that causes the error. The following example illustrates how to check for errors:


Example 11–1 Checking Error State


#include <iostream.h>
enum iostate {IOok, IOeof, IOfail};

iostate read_number(istream& istr, int& num)
{
    stream_locker sl(istr, stream_locker::lock_now);

    istr >> num;

    if (istr.eof()) return IOeof;
    if (istr.fail()) return IOfail;
    return IOok;
}

In this example, the constructor of the stream_locker object sl locks the istream object istr. The destructor of sl, called at the termination of read_number, unlocks istr.

Obtaining Characters Extracted by Last Unformatted Input Operation

To be MT-safe, the gcount function must be called within a thread that has exclusive use of the istream object for the period that includes the execution of the last input operation and gcount call. The following example shows a call to gcount:


Example 11–2 Calling gcount


#include <iostream.h>
#include <rlocks.h>
void fetch_line(istream& istr, char* line, int& linecount)
{
    stream_locker sl(istr, stream_locker::lock_defer);

    sl.lock(); // lock the stream istr
    istr >> line;
    linecount = istr.gcount();
    sl.unlock(); // unlock istr
    ...
}

In this example, the lock and unlock member functions of class stream_locker define a mutual exclusion region in the program.

User-Defined I/O Operations

To be MT-safe, I/O operations defined for a user-defined type that involve a specific ordering of separate operations must be locked to define a critical region. The following example shows a user-defined I/O operation:


Example 11–3 User-Defined I/O Operations


#include <rlocks.h>
#include <iostream.h>
class mystream: public istream {

    // other definitions...
    int getRecord(char* name, int& id, float& gpa);
};

int mystream::getRecord(char* name, int& id, float& gpa)
{
    stream_locker sl(this, stream_locker::lock_now);

    *this >> name;
    *this >> id;
    *this >> gpa;

    return this->fail() == 0;
}

11.4.1.4 Reducing Performance Overhead of MT-Safe Classes

Using the MT-safe classes in this version of the libC library results in some amount of performance overhead, even in a single-threaded application; however, if you use the unsafe_ classes of libC, this overhead can be avoided.

The scope resolution operator can be used to execute member functions of the base unsafe_ classes; for example:


    cout.unsafe_ostream::put(’4’);

    cin.unsafe_istream::read(buf, len);

Note –

The unsafe_ classes cannot be safely used in multithreaded applications.


Instead of using unsafe_ classes, you can make the cout and cin objects unsafe and then use the normal operations. A slight performance deterioration results. The following example shows how to use unsafe cout and cin:


Example 11–4 Disabling MT-Safety


#include <iostream.h>
//disable mt-safety
cout.set_safe_flag(stream_MT::unsafe_object);    
//disable mt-safety
cin.set_safe_flag(stream_MT::unsafe_object);    
cout.put(”4’);
cin.read(buf, len);

When an iostream object is MT-safe, mutex locking is provided to protect the object’s member variables. This locking adds unnecessary overhead to an application that only executes in a single-threaded environment. To improve performance, you can dynamically switch an iostream object to and from MT-safety. The following example makes an iostream object MT-unsafe:


Example 11–5 Switching to MT-Unsafe


fs.set_safe_flag(stream_MT::unsafe_object);// disable MT-safety
    .... do various i/o operations

You can safely use an MT-unsafe stream in code where an iostream is not shared by threads; for example, in a program that has only one thread, or in a program where each iostream is private to a thread.

If you explicitly insert synchronization into the program, you can also safely use MT-unsafe iostreams in an environment where an iostream is shared by threads. The following example illustrates the technique:


Example 11–6 Using Synchronization With MT-Unsafe Objects


    generic_lock();
    fs.set_safe_flag(stream_MT::unsafe_object);
    ... do various i/o operations
    generic_unlock();

where the generic_lock and generic_unlock functions can be any synchronization mechanism that uses such primitives as mutex, semaphores, or reader/writer locks.


Note –

The stream_locker class provided by the libC library is the preferred mechanism for this purpose.


See 11.4.5 Object Locks for more information.