diff packages/compat/posix/current/src/mqueue.cxx @ 115:6ed91473a1cd ecos-sw-2000-08-21

Merge from eCos master repository on 2000-08-21-22:40:54-BST
author jlarmour
date Fri, 25 Aug 2000 17:32:38 +0000
parents
children 022f1e506033
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/compat/posix/current/src/mqueue.cxx
@@ -0,0 +1,786 @@
+/*========================================================================
+//
+//      mqueue.cxx
+//
+//      Message queues tests
+//
+//========================================================================
+//####COPYRIGHTBEGIN####
+//                                                                          
+// -------------------------------------------                              
+// The contents of this file are subject to the Red Hat eCos Public License 
+// Version 1.1 (the "License"); you may not use this file except in         
+// compliance with the License.  You may obtain a copy of the License at    
+// http://www.redhat.com/                                                   
+//                                                                          
+// Software distributed under the License is distributed on an "AS IS"      
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the 
+// License for the specific language governing rights and limitations under 
+// the License.                                                             
+//                                                                          
+// The Original Code is eCos - Embedded Configurable Operating System,      
+// released September 30, 1998.                                             
+//                                                                          
+// The Initial Developer of the Original Code is Red Hat.                   
+// Portions created by Red Hat are                                          
+// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.                             
+// All Rights Reserved.                                                     
+// -------------------------------------------                              
+//                                                                          
+//####COPYRIGHTEND####
+//========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):     jlarmour
+// Contributors:  
+// Date:          2000-05-14
+// Purpose:       This file provides the implementation for POSIX message
+//                queues
+// Description:   It uses eCos kernel mqueues as the underlying
+//                implementation
+// Usage:         
+//
+//####DESCRIPTIONEND####
+//
+//======================================================================
+*/
+
+/* CONFIGURATION */
+
+#include <pkgconf/posix.h>
+
+#ifdef CYGPKG_POSIX_MQUEUES
+
+#include <pkgconf/kernel.h>
+
+/* INCLUDES */
+
+#include <cyg/infra/cyg_type.h>      // common types etc.
+#include <cyg/infra/cyg_ass.h>       // Assertion support
+#include <cyg/infra/cyg_trac.h>      // Tracing support
+#include <cyg/kernel/mqueue.hxx>     // eCos Mqueue Header
+#include <cyg/kernel/sched.hxx>      // Cyg_Scheduler::lock()
+#include <cyg/kernel/sched.inl>      // inlines for above
+#include <mqueue.h>                  // Standard POSIX mqueue header
+#include <sys/types.h>               // mode_t, ssize_t
+#include <limits.h>                  // PATH_MAX
+#include <stdlib.h>                  // malloc, etc.
+#include <errno.h>                   // errno
+#include <fcntl.h>                   // O_*
+#include <stdarg.h>                  // varargs
+#include <pthread.h>                 // mutexes
+#include <string.h>                  // strncpy
+#include <new>                       // C++ new
+#ifdef CYGFUN_POSIX_MQUEUE_NOTIFY
+# include <signal.h>
+# include "pprivate.h"               // cyg_sigqueue()
+#endif
+
+
+/* CONSTANTS */
+
+#define MQ_VALID_MAGIC  0x6db256c1
+
+/* TYPE DEFINITIONS */
+
+struct mqtabent;
+
+// this is a queue user - each one of these corresponds to a mqd_t
+struct mquser {
+    int flags;               // O_RDONLY, O_WRONLY, O_RDWR, O_NONBLOCK
+    struct mqtabent *tabent; // back pointer to table entry
+    struct mquser *next;
+    bool notifieruser;       // POSIX sucks so bad. It requires a mq_close
+                             // to only deregister the notification if it
+                             // was done via this descriptor. So we have to
+                             // know if it was this one
+#ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
+    cyg_uint32 magic;        // magic number: MQ_VALID_MAGIC if valid
+#endif    
+};
+
+struct mqtabent {
+    char name[ PATH_MAX ]; // ascii name - set to "" when unused
+    Cyg_Mqueue *mq;        // the underlying queue object
+    long maxmsg;           // as set on creation
+    long msgsize;          // as set on creation
+    bool unlinkme;         // unlink when final user closes?
+    struct mquser *users;  // each user
+
+#ifdef CYGFUN_POSIX_MQUEUE_NOTIFY
+    const struct sigevent *sigev; // notification event
+#endif
+};
+
+/* GLOBALS */
+
+static struct mqtabent mqtab[ CYGNUM_POSIX_MQUEUE_OPEN_MAX ];
+static pthread_mutex_t mqtab_mut = PTHREAD_MUTEX_INITIALIZER;
+
+/* LOCAL FUNCTIONS */
+
+//------------------------------------------------------------------------
+
+// Deallocation callback from Cyg_Mqueue
+static void
+my_free( void *ptr, size_t )
+{
+    free( ptr );
+}
+
+//------------------------------------------------------------------------
+
+// Do the actual "unlink" of a queue, i.e. mark it invalid in the table.
+// The table mutex is assumed to be locked
+static void
+do_mq_unlink( struct mqtabent *tabent )
+{
+    CYG_REPORT_FUNCTION();
+    CYG_CHECK_DATA_PTRC( tabent );
+
+    tabent->name[0] = '\0'; // won't match anything the user sends now
+    tabent->mq->~Cyg_Mqueue();
+    free( tabent->mq );
+    
+    CYG_REPORT_RETURN();
+}
+
+//------------------------------------------------------------------------
+
+#ifdef CYGFUN_POSIX_MQUEUE_NOTIFY
+
+static void
+notifyme( Cyg_Mqueue &q, CYG_ADDRWORD data )
+{
+    CYG_REPORT_FUNCTION();
+    struct mquser *user = (struct mquser *)data;
+    CYG_CHECK_DATA_PTRC( user );
+    struct mqtabent *tabent = user->tabent;
+    CYG_CHECK_DATA_PTRC( tabent );
+
+    Cyg_Scheduler::lock();
+    // we may have been pre-empted before this, so check there's still a
+    // notification to do
+
+    if ( NULL == tabent->sigev ) {
+        Cyg_Scheduler::unlock();
+        CYG_REPORT_RETURN();
+        return;
+    } // if
+
+    const struct sigevent *ev = tabent->sigev;
+    
+    // first deregister
+    q.setnotify( NULL, 0 );
+    tabent->sigev = NULL;
+    user->notifieruser = false; // not any more
+    
+    // now the rest of the world can go
+    Cyg_Scheduler::unlock();
+    
+    // queue event. If it fails... nothing we can do :-( so ignore return code
+    cyg_sigqueue( ev, SI_MESGQ );
+
+    cyg_deliver_signals();
+    
+    CYG_REPORT_RETURN();
+}
+
+#endif // ifdef CYGFUN_POSIX_MQUEUE_NOTIFY
+
+//------------------------------------------------------------------------
+
+/* EXPORTED FUNCTIONS */
+
+externC mqd_t
+mq_open( const char *name, int oflag, ... )
+{
+    CYG_REPORT_FUNCTYPE( "returning %08x" );
+    CYG_REPORT_FUNCARG2( "name=%08x, oflag=%d", name, oflag );
+    CYG_CHECK_DATA_PTRC( name );
+
+    if ( ((oflag & O_RDONLY) != O_RDONLY) &&
+         ((oflag & O_WRONLY) != O_WRONLY) &&
+         ((oflag & O_RDWR) != O_RDWR)) {
+        // user didn't specify mode
+        errno = EINVAL;
+        CYG_REPORT_RETVAL( -1 );
+        return (mqd_t)-1;
+    } // if
+
+    mqd_t retval;
+    cyg_ucount32 i;
+    struct mqtabent *qtabent=NULL;
+    int interr;
+
+    interr = pthread_mutex_lock( &mqtab_mut );
+    // should never fail
+    CYG_ASSERT( interr == 0, "internal lock failed!" );
+    
+    // find if a matching entry exists first
+    // FIXME: Should check for length and return ENAMETOOLONG
+    for ( i=0; i < CYGNUM_POSIX_MQUEUE_OPEN_MAX; i++ ) {
+        if ( 0 == strncmp(name, mqtab[i].name, PATH_MAX) ) {
+            qtabent = &mqtab[i];
+            break;
+        } // if
+    } // for
+    
+    if ( (NULL != qtabent) && (O_EXCL == (oflag & O_EXCL)) ) {
+        errno = EEXIST;
+        retval = (mqd_t)-1;
+        goto exit_unlock;
+    }
+        
+    if ( (NULL == qtabent) && (O_CREAT != (oflag & O_CREAT)) ) {
+        errno = ENOENT;
+        retval = (mqd_t)-1;
+        goto exit_unlock;
+    }
+
+    // so if we didn't find something, we must be being asked to create it
+    if (NULL == qtabent) {
+        mode_t mode; // FIXME: mode ignored for now
+        const struct mq_attr *attr;
+        const struct mq_attr default_attr = { 0, MQ_OPEN_MAX, 128 };
+        va_list args;
+        
+        va_start( args, oflag );
+        mode = va_arg( args, mode_t );
+        attr = va_arg( args, struct mq_attr * );
+        va_end( args );
+
+        // find an empty table entry
+        for ( i=0; i < CYGNUM_POSIX_MQUEUE_OPEN_MAX; i++ ) {
+            if ( NULL == mqtab[i].mq )
+                break;
+        }
+
+        // if not found, table is full
+        if ( i == CYGNUM_POSIX_MQUEUE_OPEN_MAX ) {
+            errno = ENFILE;
+            retval = (mqd_t)-1;
+            goto exit_unlock;
+        }
+
+        Cyg_Mqueue::qerr_t qerr;
+
+        // user can specify NULL attr, which means arbitrary message queue
+        // size! Duh.
+        if ( NULL == attr )
+            attr = &default_attr;
+        else {
+            // if they do supply one, POSIX says we're meant to check it
+            if (attr->mq_maxmsg <= 0 || attr->mq_msgsize <= 0) {
+                errno = EINVAL;
+                retval = (mqd_t)-1;
+                goto exit_unlock;
+            }
+        } // else
+
+        // allocate the underlying queue
+        Cyg_Mqueue *mqholder = (Cyg_Mqueue *)malloc( sizeof(Cyg_Mqueue) );
+        if ( NULL == mqholder ) {
+            errno = ENOSPC;
+            retval = (mqd_t)-1;
+            goto exit_unlock;
+        }
+            
+        // construct it with placement new
+        mqtab[i].mq = new (mqholder) Cyg_Mqueue( attr->mq_maxmsg,
+                                                 attr->mq_msgsize,
+                                                 &malloc, &my_free, &qerr );
+            
+        switch (qerr) {
+        case Cyg_Mqueue::OK:
+            break;
+        case Cyg_Mqueue::NOMEM:
+            free( mqholder );
+            errno = ENOSPC;
+            retval = (mqd_t)-1;
+            goto exit_unlock;
+        default:
+            CYG_FAIL("Unhandled Cyg_Mqueue constructor return error");
+            break;
+        } // switch
+
+        mqtab[i].users = (struct mquser *) malloc( sizeof(struct mquser) );
+        if ( NULL == mqtab[i].users ) {
+            mqtab[i].mq->~Cyg_Mqueue();
+            free( mqholder );
+            errno = ENOSPC;
+            retval = (mqd_t)-1;
+            goto exit_unlock;
+        }
+
+        // initialize mqtab[i]
+        mqtab[i].maxmsg  = attr->mq_maxmsg;
+        mqtab[i].msgsize = attr->mq_msgsize;
+        mqtab[i].unlinkme = false;
+        mqtab[i].sigev = NULL;
+        strncpy( mqtab[i].name, name, PATH_MAX );
+
+        // initialize first mqtab[i].users
+        mqtab[i].users->next = NULL;
+        // set the mode for later, but also note that O_NONBLOCK can
+        // be set in oflags *or* the attr the user passed
+        mqtab[i].users->flags = oflag | (attr->mq_flags & O_NONBLOCK);
+
+        // set back pointer so that message queue handle can find actual queue
+        mqtab[i].users->tabent = &mqtab[i]; 
+        
+        mqtab[i].users->notifieruser = false;
+
+#ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
+        mqtab[i].users->magic = MQ_VALID_MAGIC; // now valid
+#endif
+        
+        retval=(mqd_t)mqtab[i].users;
+
+        goto exit_unlock;
+    } // if (NULL == qtabent)
+
+    // so we're not creating, and we have a valid qtabent
+
+    // But this qtabent may be being unlinked. If so, we are permitted
+    // to return an error, so we will. (see under mq_unlink() in POSIX)
+    // Which error though? EINVAL seems best, but POSIX doesn't say :-/
+
+    if (true == qtabent->unlinkme) {
+        errno = EINVAL;
+        retval = (mqd_t)-1;
+        goto exit_unlock;
+    }
+    
+    // now we have a usable qtabent
+
+    struct mquser *user;
+    user = (struct mquser *) malloc( sizeof(struct mquser) );
+    if ( NULL == user ) {
+            errno = ENOSPC;
+            retval = (mqd_t)-1;
+            goto exit_unlock;
+    }
+
+    // prepend to qtab user list
+    user->next = qtabent->users;
+    qtabent->users = user;
+
+    // set back pointer so that message queue handle can find actual queue
+    user->tabent = qtabent;
+
+    user->flags = oflag;
+
+#ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
+    user->magic = MQ_VALID_MAGIC; // now valid
+#endif
+
+    retval=(mqd_t)user;
+
+ exit_unlock:
+    interr = pthread_mutex_unlock( &mqtab_mut );
+    // should never fail
+    CYG_ASSERT( interr == 0, "internal lock failed!" );
+    CYG_REPORT_RETVAL( retval );
+    return retval;
+} // mq_open()
+
+//------------------------------------------------------------------------
+
+// NOTE: It is the *user*'s responsibility to ensure that nothing is
+// blocked in mq_send() or mq_receive() when closing the queue with
+// that descriptor. The standard does not specify the behaviour, so that's
+// what I am assuming
+
+externC int
+mq_close( mqd_t mqdes )
+{
+    CYG_REPORT_FUNCTYPE( "returning %d" );
+    CYG_REPORT_FUNCARG1XV( mqdes );
+    
+    struct mquser *user = (struct mquser *)mqdes;
+
+#ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
+    if ( user->magic != MQ_VALID_MAGIC ) {
+        errno  = EBADF;
+        CYG_REPORT_RETVAL( -1 );
+        return -1;
+    }
+#endif
+    
+    int interr;
+
+    interr = pthread_mutex_lock( &mqtab_mut );
+    // should never fail
+    CYG_ASSERT( interr == 0, "internal lock failed!" );
+
+    struct mqtabent *tabent = user->tabent;
+    struct mquser *usertmp;
+    
+    // perhaps should return EBADF instead of assert?
+    CYG_ASSERT( tabent->users != NULL, "Null message queue user list" );
+
+    // deregister notification iff this was the message queue descriptor
+    // that was used to register it (POSIX says)
+    if ( true == user->notifieruser ) {
+        tabent->mq->setnotify( NULL, 0 );
+        tabent->sigev = NULL;
+        // not worth clearing notifieruser
+    }
+
+    // find in the list for this queue and remove - sucks a bit, but seems
+    // best over all - the list shouldn't be too long
+    if ( tabent->users == user ) {
+        tabent->users = user->next;  // remove
+    } else {
+        for ( usertmp=tabent->users;
+              NULL != usertmp->next;
+              usertmp = usertmp->next ) {
+            if ( usertmp->next == user )
+                break;
+        } // for
+
+        // perhaps should return EBADF instead of assert?
+        CYG_ASSERT( usertmp->next != NULL, "Couldn't find message queue user" );
+
+        usertmp->next = user->next; // remove
+    } // else
+    
+#ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
+    user->magic = 0; // invalidate
+#endif
+
+    // free it up
+    free( user );
+
+    if ( (true == tabent->unlinkme) && (NULL == tabent->users) ) {
+        do_mq_unlink( tabent );
+    } // if
+
+    interr = pthread_mutex_unlock( &mqtab_mut );
+    // should never fail
+    CYG_ASSERT( interr == 0, "internal lock failed!" );
+    CYG_REPORT_RETVAL( 0 );
+    return 0;
+} // mq_close()
+
+
+//------------------------------------------------------------------------
+
+externC int
+mq_unlink( const char *name )
+{
+    CYG_REPORT_FUNCTYPE( "returning %d" );
+    CYG_REPORT_FUNCARG1( "name=%s", name );
+
+    int retval, interr;
+    cyg_ucount32 i;
+    struct mqtabent *qtabent=NULL;
+
+    interr = pthread_mutex_lock( &mqtab_mut );
+    // should never fail
+    CYG_ASSERT( interr == 0, "internal lock failed!" );
+
+    // find the entry first
+    // FIXME: Should check for length and return ENAMETOOLONG
+    for ( i=0; i < CYGNUM_POSIX_MQUEUE_OPEN_MAX; i++ ) {
+        if ( 0 == strncmp(name, mqtab[i].name, PATH_MAX) ) {
+            qtabent = &mqtab[i];
+            break;
+        } // if
+    } // for
+
+    if ( NULL == qtabent ) { // not found
+        errno = ENOENT;
+        retval = -1;
+        goto exit_unlock;
+    }
+
+    if ( NULL != qtabent->users ) {   // still in use
+        qtabent->unlinkme = true;     // so mark it as pending deletion
+    } else {
+        do_mq_unlink( qtabent );
+    } // else
+
+    retval = 0;
+
+ exit_unlock:
+    interr = pthread_mutex_unlock( &mqtab_mut );
+    // should never fail
+    CYG_ASSERT( interr == 0, "internal lock failed!" );
+    CYG_REPORT_RETVAL( retval );
+    return retval;
+} // mq_unlink()
+
+//------------------------------------------------------------------------
+
+externC int
+mq_send( mqd_t mqdes, const char *msg_ptr, size_t msg_len,
+         unsigned int msg_prio )
+{
+    CYG_REPORT_FUNCTYPE( "returning %d" );
+    CYG_REPORT_FUNCARG4( "mqdes=%08x, msg_ptr=%08x, msg_len=%u, msg_prio=%u",
+                         mqdes, msg_ptr, msg_len, msg_prio );
+    CYG_CHECK_DATA_PTRC( msg_ptr );
+    
+    struct mquser *user = (struct mquser *)mqdes;
+    struct mqtabent *tabent = user->tabent;
+
+#ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
+    if ( user->magic != MQ_VALID_MAGIC ) {
+        errno  = EBADF;
+        CYG_REPORT_RETVAL( -1 );
+        return -1;
+    }
+#endif
+    
+    if ( msg_len > (size_t)tabent->msgsize ) {
+        errno = EMSGSIZE;
+        CYG_REPORT_RETVAL( -1 );
+        return -1;
+    }
+
+    if ( msg_prio > MQ_PRIO_MAX ) {
+        errno = EINVAL;
+        CYG_REPORT_RETVAL( -1 );
+        return -1;
+    }
+
+    if ( (O_WRONLY != (user->flags & O_WRONLY)) && 
+         (O_RDWR != (user->flags & O_RDWR)) ) {
+        errno = EBADF;
+        CYG_REPORT_RETVAL( -1 );
+        return -1;
+    }
+
+    // go for it
+    Cyg_Mqueue::qerr_t err;
+    err = tabent->mq->put( msg_ptr, msg_len, msg_prio,
+                           ((user->flags & O_NONBLOCK) != O_NONBLOCK) );
+    switch (err) {
+
+    case Cyg_Mqueue::INTR:
+        errno = EINTR;
+        CYG_REPORT_RETVAL( -1 );
+        return -1;
+
+    case Cyg_Mqueue::WOULDBLOCK:
+        CYG_ASSERT( (user->flags & O_NONBLOCK) == O_NONBLOCK,
+                    "Message queue assumed non-blocking when blocking requested"
+            );
+        errno = EAGAIN;
+        CYG_REPORT_RETVAL( -1 );
+        return -1;
+        
+    case Cyg_Mqueue::OK:
+        CYG_REPORT_RETVAL( 0 );
+        return 0;
+
+    default:
+        CYG_FAIL( "unhandled message queue return code" );
+        return -1; // keep compiler happy
+    } // switch
+} // mq_send()
+
+//------------------------------------------------------------------------
+
+
+externC ssize_t
+mq_receive( mqd_t mqdes, char *msg_ptr, size_t msg_len,
+            unsigned int *msg_prio )
+{
+    CYG_REPORT_FUNCTYPE( "returning %ld" );
+    CYG_REPORT_FUNCARG4( "mqdes=%08x, msg_ptr=%08x, msg_len=%u, msg_prio=%08x",
+                         mqdes, msg_ptr, msg_len, msg_prio );
+    CYG_CHECK_DATA_PTRC( msg_ptr );
+    CYG_CHECK_DATA_PTRC( msg_ptr+msg_len-1 );
+    if ( NULL != msg_prio )
+        CYG_CHECK_DATA_PTRC( msg_prio );
+    
+    
+    struct mquser *user = (struct mquser *)mqdes;
+    struct mqtabent *tabent = user->tabent;
+
+#ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
+    if ( user->magic != MQ_VALID_MAGIC ) {
+        errno  = EBADF;
+        CYG_REPORT_RETVAL( -1 );
+        return (ssize_t)-1;
+    }
+#endif
+    
+    if ( (O_RDONLY != (user->flags & O_RDONLY)) && 
+         (O_RDWR != (user->flags & O_RDWR)) ) {
+        errno = EBADF;
+        CYG_REPORT_RETVAL( -1 );
+        return (ssize_t)-1;
+    }
+
+    if ( msg_len < (size_t)tabent->msgsize ) {
+        errno = EMSGSIZE;
+        CYG_REPORT_RETVAL( -1 );
+        return (ssize_t)-1;
+    }
+
+    // go for it
+    Cyg_Mqueue::qerr_t err;
+    err = tabent->mq->get( msg_ptr, &msg_len, msg_prio,
+                           ((user->flags & O_NONBLOCK) != O_NONBLOCK) );
+    switch (err) {
+
+    case Cyg_Mqueue::INTR:
+        errno = EINTR;
+        CYG_REPORT_RETVAL( -1 );
+        return (ssize_t)-1;
+
+    case Cyg_Mqueue::WOULDBLOCK:
+        CYG_ASSERT( (user->flags & O_NONBLOCK) == O_NONBLOCK,
+                    "Message queue assumed non-blocking when blocking requested"
+            );
+        errno = EAGAIN;
+        CYG_REPORT_RETVAL( -1 );
+        return (ssize_t)-1;
+        
+    case Cyg_Mqueue::OK:
+        CYG_ASSERT( msg_len <= (size_t)tabent->msgsize,
+                    "returned message too long" );
+        if ( NULL != msg_prio )
+            CYG_ASSERT( *msg_prio <= MQ_PRIO_MAX,
+                        "returned message has invalid priority" );
+        CYG_REPORT_RETVAL( msg_len );
+        return (ssize_t)msg_len;
+
+    default:
+        CYG_FAIL( "unhandled message queue return code" );
+        return (ssize_t)-1; // keep compiler happy
+    } // switch
+    
+} // mq_receive()
+
+//------------------------------------------------------------------------
+
+#ifdef CYGFUN_POSIX_MQUEUE_NOTIFY
+
+externC int
+mq_notify( mqd_t mqdes, const struct sigevent *notification )
+{
+    CYG_REPORT_FUNCTYPE( "returning %d" );
+    CYG_REPORT_FUNCARG2( "mqdes=%08x, notification=%08x", mqdes, notification );
+    if ( NULL != notification )
+        CYG_CHECK_DATA_PTRC( notification );
+    
+    struct mquser *user = (struct mquser *)mqdes;
+    struct mqtabent *tabent = user->tabent;
+
+#ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
+    if ( user->magic != MQ_VALID_MAGIC ) {
+        errno  = EBADF;
+        CYG_REPORT_RETVAL( -1 );
+        return -1;
+    }
+#endif
+    
+    // lock scheduler since we test and set non-atomically
+    Cyg_Scheduler::lock();
+    
+    // we are being told to clear the notification function
+    if ( NULL == notification ) {
+        tabent->mq->setnotify( NULL, 0 );
+        tabent->sigev = NULL;
+        Cyg_Scheduler::unlock();
+        CYG_REPORT_RETVAL( 0 );
+        return 0;
+    } // if
+    
+    if ( NULL != tabent->sigev ) {  // already registered
+        Cyg_Scheduler::unlock();
+        errno = EBUSY;
+        CYG_REPORT_RETVAL( -1 );
+        return -1;
+    } // if
+
+    tabent->sigev = notification;
+    user->notifieruser = true; // Used for deciding about whether to
+                               // deregister in mq_close()
+    tabent->mq->setnotify( &notifyme, (CYG_ADDRWORD) user );
+    Cyg_Scheduler::unlock();
+    
+    CYG_REPORT_RETVAL( 0 );
+    return 0;
+} // mq_notify()
+
+#endif // ifdef CYGFUN_POSIX_MQUEUE_NOTIFY
+
+//------------------------------------------------------------------------
+
+externC int
+mq_setattr( mqd_t mqdes, const struct mq_attr *mqstat,
+            struct mq_attr *omqstat )
+{
+    CYG_REPORT_FUNCTYPE( "returning %d" );
+    CYG_REPORT_FUNCARG3( "mqdes=%08x, mqstat=%08x, omqstat=%08x",
+                         mqdes, mqstat, omqstat );
+    CYG_CHECK_DATA_PTRC( mqstat );
+
+    struct mquser *user = (struct mquser *)mqdes;
+
+#ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
+    if ( user->magic != MQ_VALID_MAGIC ) {
+        errno  = EBADF;
+        CYG_REPORT_RETVAL( -1 );
+        return -1;
+    }
+#endif
+    
+    if ( NULL != omqstat ) {
+        CYG_CHECK_DATA_PTRC( omqstat );
+        mq_getattr( mqdes, omqstat );
+    } // if
+
+    // Two-stage update, so lock sched since it's quick
+    Cyg_Scheduler::lock();
+    user->flags &= ~O_NONBLOCK;  // clear
+    if ( (mqstat->mq_flags & O_NONBLOCK) == O_NONBLOCK ) {
+        user->flags |= O_NONBLOCK;
+    } // if
+    Cyg_Scheduler::unlock();
+
+    CYG_REPORT_RETVAL( 0 );
+    return 0;
+} // mq_setattr()
+
+//------------------------------------------------------------------------
+
+externC int
+mq_getattr( mqd_t mqdes, struct mq_attr *mqstat )
+{
+    CYG_REPORT_FUNCTYPE( "returning %d" );
+    CYG_REPORT_FUNCARG2( "mqdes=%08x, mqstat=%08x", mqdes, mqstat );
+    CYG_CHECK_DATA_PTRC( mqstat );
+
+    struct mquser *user = (struct mquser *)mqdes;
+    struct mqtabent *tabent = user->tabent;
+
+#ifdef CYGIMP_POSIX_MQUEUE_VALIDATE_DESCRIPTOR
+    if ( user->magic != MQ_VALID_MAGIC ) {
+        errno  = EBADF;
+        CYG_REPORT_RETVAL( -1 );
+        return -1;
+    }
+#endif
+    
+    mqstat->mq_flags   = user->flags;
+    mqstat->mq_maxmsg  = tabent->maxmsg;
+    mqstat->mq_msgsize = tabent->msgsize;
+    mqstat->mq_curmsgs = tabent->mq->count();    
+    
+    CYG_REPORT_RETVAL( 0 );
+    return 0;
+} // mq_getattr()
+
+
+//------------------------------------------------------------------------
+
+#endif // ifdef CYGPKG_POSIX_MQUEUES
+
+/* EOF mqueue.cxx */