Sorry, the first try was badly formatted. I didn't realize
until I checked the archive site.

I've been debugging a problem with msgsnd() hanging. If
there are no free msghdrs available, msgsnd() blocks with
msleep(). Unfortunately, the only way it can unblock is
if that specific queue frees a msghdr. If the queue in
question is empty, this never occurs. I was able to isolate
this behavior in set of examples, which I've attached. The
program doqueue.c establishes a queue and upon each ENTER,
calls msgsnd() followed by msgrcv(). Start this one first
and verify that messages cycle normally.

In another window, run overflow.c. This establishes a
different queue, and calls msgsnd() until the queue is full,
then exits. In the default configuration, that will happen at
40 messages. Go back to the first window and press ENTER. the
call to msgsnd() will block. Then, either run drainq to remove
messages from overflow's queue, or use ipcrm -q 4660 to delete
the queue entirely. Doqueue will remain blocked.

It's possible to work around this by using the flag IPC_NOWAIT
in msgsnd, and polling until the message is sent, but my feeling
is that the library call should not hang like this.

Here is the code in question:

From sysv_msg.cc, function msgsnd():
=========
if (free_msghdrs == NULL) {
        DPRINTF(("no more msghdrs\n"));
        need_more_resources = 1;
}

if (need_more_resources) {
        int we_own_it;

        if ((msgflg & IPC_NOWAIT) != 0) {
                DPRINTF(("need more resources but caller "
                    "doesn't want to wait\n"));
                error = EAGAIN;
                goto done2;
        }

        if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) {
                DPRINTF(("we don't own the msqid_ds\n"));
                we_own_it = 0;
        } else {
                /* Force later arrivals to wait for our
                   request */
                DPRINTF(("we own the msqid_ds\n"));
                msqptr->msg_perm.mode |= MSG_LOCKED;
                we_own_it = 1;
        }
        DPRINTF(("goodnight\n"));
        error = msleep(msqptr, &msq_mtx, (PZERO - 4) | PCATCH,
            "msgwait", 0);
        DPRINTF(("good morning, error=%d\n", error));
==========

The call to msleep() above passes msqptr (the queue handle)
as the Ident pointer. Each of the calls to wakeup() in
sysv_msg.cc also passes msgptr as the ident. This means that
if the msghdr resource is free'd by a queue other than the one
blocked, it won't wake up msgsnd(). Since doqueue's queue is
empty, there is no way to wake up msgsnd().

Here is a snippet from /var/log/messages:
Apr 29 13:38:16 motonao cygserver: call to msgsnd(131072, 0x22CCD8, 1, 0)
Apr 29 13:38:16 motonao cygserver: msgsz=1, msgssz=8, segs_needed=1
Apr 29 13:38:16 motonao cygserver: no more msghdrs
Apr 29 13:38:16 motonao cygserver: we own the msqid_ds
Apr 29 13:38:16 motonao cygserver: goodnight
Apr 29 13:38:24 motonao cygserver: msgget(0x1234, 00)
Apr 29 13:38:24 motonao cygserver: found public key
Apr 29 13:38:24 motonao cygserver: call to msgrcv(196609, 0x22CCD8, 1, 0, 0)
Apr 29 13:38:24 motonao cygserver: found a message, msgsz=1, msg_ts=1

The first line is doqueue's last call to msgsnd(). It finds
there are no free msghdrs and logs the message "no more msghdrs",
then logs "goodnight" and calls msleep(). The call to msgrcv is
drainq removing a message from the overflow queue. This is the
point where we would like to see the "good morning" message from
msgsnd(), but we don't.

I haven't been able to spot a way to fix this behavior without
significantly changing the block/release mechanism. Has anyone
seen this before? Have I missed something? Is this simply a known
limitation, with IPC_NOWAIT the only way to deal with it?

~

David Williams
Solekai Systems


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>

#define MSGQ_KEY 0x1234
#define PERM 0660

struct mymsg {
  long mtype;
  char mtext[1];
};

int
main()
{
  int msg_id; /* message Queue id. */
  struct mymsg msg; /* message to send. */
  int nmsg; /* number of msg send. */
  int status; /* status returned by msgsnd(). */

  /*
   * Create Message Queue
   */
  if ( (msg_id = msgget (MSGQ_KEY, PERM | IPC_CREAT)) == -1 ) {
    perror ("msgget: ");
    exit (EXIT_FAILURE);
  }
  /*
   * now write message one by one until Queue is full
   */
  msg.mtype = 1;
  msg.mtext[0] = 'A';
  nmsg = 0;
  do {
    printf ("Receiving #%d\n", nmsg);
    status = msgrcv (msg_id, &msg, sizeof(msg.mtext), 0, 0);
    printf ("Received msg #%d\n", nmsg);
    nmsg++;
  }
  while ( tolower(getchar()) != 'q' );
  
  
  
  exit (EXIT_SUCCESS);
} 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>

#define MSGQ_KEY 0x1234
#define PERM 0660

struct mymsg {
  long mtype;
  char mtext[1];
};

int
main()
{
  int msg_id; /* message Queue id. */
  struct mymsg msg; /* message to send. */
  int nmsg; /* number of msg send. */
  int status; /* status returned by msgsnd(). */

  /*
   * Create Message Queue
   */
  if ( (msg_id = msgget (MSGQ_KEY, PERM | IPC_CREAT)) == -1 ) {
    perror ("msgget: ");
    exit (EXIT_FAILURE);
  }
  /*
   * now write message one by one until Queue is full
   */
  msg.mtype = 1;
  msg.mtext[0] = 'A';
  nmsg = 0;
  do {
    status = msgsnd (msg_id, &msg, sizeof(msg.mtext), IPC_NOWAIT);
    nmsg++;
    printf ("Sent msg #%d\r", nmsg);
  }
  while ( status == 0 );
  
  if (errno != EAGAIN ) { /* eh? we didn't overflow? */
    perror ("msgsnd: ");
    exit (EXIT_FAILURE);
  }
  
  printf ("Queue full, sent %d messages\n", nmsg-1);
  exit (EXIT_SUCCESS);
} 
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>

#define MSGQ_KEY 0x4321
#define PERM 0660

struct mymsg {
  long mtype;
  char mtext[1];
};

int
main()
{
  int msg_id; /* message Queue id. */
  struct mymsg msg; /* message to send. */
  int nmsg; /* number of msg send. */
  int status; /* status returned by msgsnd(). */

  /*
   * Create Message Queue
   */
  if ( (msg_id = msgget (MSGQ_KEY, PERM | IPC_CREAT)) == -1 ) {
    perror ("msgget: ");
    exit (EXIT_FAILURE);
  }
  /*
   * now write message one by one until Queue is full
   */
  msg.mtype = 1;
  msg.mtext[0] = 'A';
  nmsg = 0;
  do {
    printf ("Sending #%d\n", nmsg);
    status = msgsnd (msg_id, &msg, sizeof(msg.mtext), 0);
    printf ("Sent msg #%d\n", nmsg);
    printf ("Receiving #%d\n", nmsg);
    status = msgrcv (msg_id, &msg, sizeof(msg.mtext), 0, 0);
    printf ("Received msg #%d\n", nmsg);
    nmsg++;
  }
  while ( tolower(getchar()) != 'q' );
  
  
  
  exit (EXIT_SUCCESS);
} 

Attachment: cygcheck.out
Description: cygcheck.out

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Problem reports:       http://cygwin.com/problems.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/

Reply via email to