Article 160050 of comp.os.vms:
In message <57f642$7o0@ss2.cs.mci.com>,
  Geoffrey Arnold <geoffrey.arnold@mci.com> writes:
>Tom Dockray wrote:
>> My problem is this. If I read the mailbox with a QIOW, it will block the
>> entire process, not just the thread in which the QIOW is issued.
>
>QIOW calls do not necessarily block at process level.  I've used them in
>multithreaded
>code for a couple of years for communication via VMS mailboxes.

While a user-mode QIOW doesn't totally block the process, it has a detrimental
effect upon thread scheduling.  The DECthreads scheduler still considers a
thread with the pending QIOW to be 'runnable' and gives it timeslices, denying
truly runnable threads the processor.

The code (draft 4/DCE API) below dramatically illustrates this effect.  The
time make 100 context switches goes from effectively 0 seconds normally to
20 seconds when a blocked QIOW is in a third thread.

------------------------------------------------------------------------------
/*
 * Simple program to expose characteristics of DECthreads scheduler.
 */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <iodef.h>
struct counter {
    pthread_mutex_t mutex;
    pthread_cond_t cond1, cond2;
    int ready, count;
};
/****************************************************************************/
/* Define main routine for the responder thread.  This thread waits perpetually
 * for a ready condition, decrements the counter, and clears the
 * ready condition.
 */
static void *responder ( struct counter *counter )
{
    pthread_mutex_lock ( &counter->mutex );
    for ( ; ; ) {
        while ( !counter->ready ) {
	    pthread_cond_wait ( &counter->cond1, &counter->mutex );
        }
	counter->ready = 0;
	--counter->count;
	pthread_cond_signal ( &counter->cond2 );
    }
    return (void *) 0;
}
/*****************************************************************************/
/* A dead-wait thread creates a mailbox and issues a pending read.
 */
static void *dead_wait ( struct counter *sig )
{
    int chan, status, SYS$CREMBX(), SYS$QIOW();
    short iosb[4];
    char buffer[100];
    /*
     * create a mailbox and signal when done.
     */
    chan = 0;
    status = SYS$CREMBX (0, &chan, sizeof(buffer), sizeof(buffer), 0, 0, 0, 0);
    if ( (status&1) == 0 ) exit ( status );
    pthread_mutex_lock ( &sig->mutex );
    sig->count = 0;
    pthread_cond_signal ( &sig->cond2 );
    pthread_mutex_unlock ( &sig->mutex );
    /*
     * Issue read on mailbox that will never be satisfied.
     */
    status = SYS$QIOW ( 0, chan, IO$_READVBLK, iosb, 0, 0,
	buffer, sizeof(buffer), 0, 0, 0, 0 );
    printf("Unexpect read completion on mailbox");
    exit ( status );
    return (void *) 0;
}
/*****************************************************************************/
/* Perform 50 interactions with responder, timing the action.
 */
static void time_response ( struct counter *counter )
{
    int LIB$INIT_TIMER(), LIB$SHOW_TIMER(), i;
    LIB$INIT_TIMER();
    pthread_mutex_lock ( &counter->mutex );
    counter->count = 50;
    while ( counter->count > 0 ) {
	counter->ready = 1;
	pthread_cond_signal ( &counter->cond1 );
	while ( counter->ready == 1 ) {
	    pthread_cond_wait ( &counter->cond2, &counter->mutex );
	}
    }
    pthread_mutex_unlock ( &counter->mutex );
    LIB$SHOW_TIMER();
}

int main ( int argc, char **argv )
{
    struct counter cnt;
    pthread_attr_t cnt_thread_attr;
    pthread_t cnt_thread;
    int status;
    /*
     * Initialize dummy thread to measure response time against.
     */
    pthread_mutex_init ( &cnt.mutex, pthread_mutexattr_default );
    pthread_cond_init ( &cnt.cond1, pthread_condattr_default );
    pthread_cond_init ( &cnt.cond2, pthread_condattr_default );
    cnt.ready = 0;
    pthread_attr_create ( &cnt_thread_attr );
    status = pthread_create ( &cnt_thread, cnt_thread_attr,
	(pthread_startroutine_t) responder, &cnt );
    /*
     * Check response time.
     */
    printf ( "Response time with no blocked threads:\n" );
    time_response(&cnt);
    /*
     * Create a thread with blocked read, wait for it to initialize, and
     * repeat the response time test.
     */
    cnt.count = 1;
    status = pthread_create ( &cnt_thread, cnt_thread_attr,
	(pthread_startroutine_t) dead_wait, &cnt );
    pthread_mutex_lock ( &cnt.mutex );
    while ( cnt.count ) pthread_cond_wait ( &cnt.cond2, &cnt.mutex );
    pthread_mutex_unlock ( &cnt.mutex );

    printf ( "Response time with 1 blocked thread:\n");
    time_response(&cnt);
    /*
     * Create another thread and repeat test.
     */
    cnt.count = 1;
    status = pthread_create ( &cnt_thread, cnt_thread_attr,
	(pthread_startroutine_t) dead_wait, &cnt );
    pthread_mutex_lock ( &cnt.mutex );
    while ( cnt.count ) pthread_cond_wait ( &cnt.cond2, &cnt.mutex );
    pthread_mutex_unlock ( &cnt.mutex );

    printf ( "Response time with 2 blocked threads:\n");
    time_response(&cnt);
    return 0;
}
------------------------------------------------------------------------------

David L. Jones               |      Phone:    (614) 292-6929
Ohio State Unviversity       |      Internet:
2070 Neil Ave. Rm. 122       |               jonesd@kcgl1.eng.ohio-state.edu
Columbus, OH 43210           |               vman+@osu.edu

Disclaimer: Dogs can't tell it's not bacon.


