static VALUE _send(int sflags, int argc, VALUE *argv, VALUE self) { struct posix_mq *mq = get(self, 1); struct rw_args x; VALUE buffer, prio, timeout; struct timespec expire; rb_scan_args(argc, argv, "12", &buffer, &prio, &timeout); setup_send_buffer(&x, buffer); x.des = mq->des; x.timeout = convert_timeout(&expire, timeout); x.msg_prio = NIL_P(prio) ? 0 : NUM2UINT(prio); retry: WITHOUT_GVL(xsend, &x, RUBY_UBF_IO, 0); if (x.retval < 0) { if (errno == EINTR) goto retry; if (errno == EAGAIN && (sflags & PMQ_TRY)) return Qfalse; rb_sys_fail("mq_send"); } return Qtrue; }
// ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); // http://man7.org/linux/man-pages/man2/msgsnd.2.html // // Receive a message from the message queue. VALUE sysvmq_receive(int argc, VALUE *argv, VALUE self) { VALUE type = INT2FIX(0); VALUE flags = INT2FIX(0); sysvmq_t* sysv; sysvmq_blocking_call_t blocking; if (argc > 2) { rb_raise(rb_eArgError, "Wrong number of arguments (0..2)"); } if (argc >= 1) type = argv[0]; if (argc == 2) flags = argv[1]; TypedData_Get_Struct(self, sysvmq_t, &sysvmq_type, sysv); Check_Type(type, T_FIXNUM); Check_Type(flags, T_FIXNUM); // Attach blocking call parameters to the struct passed to the blocking // function wrapper. blocking.flags = FIX2INT(flags); blocking.type = FIX2LONG(type); blocking.sysv = sysv; // Initialize error so it's never a garbage value, if // `sysvmq_maybe_blocking_receive` was interrupted at a non-nice time. blocking.error = UNINITIALIZED_ERROR; blocking.length = UNINITIALIZED_ERROR; if ((blocking.flags & IPC_NOWAIT) == IPC_NOWAIT) { while(sysvmq_maybe_blocking_receive(&blocking) == NULL && blocking.error < 0) { if (errno == EINTR) { continue; } rb_sys_fail("Failed recieving message from queue"); } } else { // msgrcv(2) can block sending a message, if IPC_NOWAIT is not passed. // We unlock the GVL waiting for the call so other threads (e.g. signal // handling) can continue to work. Sets `length` on `blocking` with the size // of the message returned. while (WITHOUT_GVL(sysvmq_maybe_blocking_receive, &blocking, RUBY_UBF_IO, NULL) == NULL && blocking.error < 0) { if (errno == EINTR || blocking.error == UNINITIALIZED_ERROR) { continue; } rb_sys_fail("Failed receiving message from queue"); } } // Guard it.. assert(blocking.length != UNINITIALIZED_ERROR); // Reencode with default external encoding return rb_str_new(sysv->msgbuf->mtext, blocking.length); }
static VALUE instrumenter_start(VALUE self) { sky_instrumenter_t* instrumenter; My_Struct(instrumenter, sky_instrumenter_t, no_instrumenter_msg); return (VALUE) WITHOUT_GVL(instrumenter_start_nogvl, instrumenter); }
static VALUE _receive(int rflags, int argc, VALUE *argv, VALUE self) { struct posix_mq *mq = get(self, 1); struct rw_args x; VALUE buffer, timeout; struct timespec expire; if (mq->attr.mq_msgsize < 0) { if (mq_getattr(mq->des, &mq->attr) < 0) rb_sys_fail("mq_getattr"); } rb_scan_args(argc, argv, "02", &buffer, &timeout); x.timeout = convert_timeout(&expire, timeout); if (NIL_P(buffer)) { buffer = rb_str_new(0, mq->attr.mq_msgsize); } else { StringValue(buffer); rb_str_modify(buffer); rb_str_resize(buffer, mq->attr.mq_msgsize); } OBJ_TAINT(buffer); x.msg_ptr = RSTRING_PTR(buffer); x.msg_len = (size_t)mq->attr.mq_msgsize; x.des = mq->des; retry: WITHOUT_GVL(xrecv, &x, RUBY_UBF_IO, 0); if (x.received < 0) { if (errno == EINTR) goto retry; if (errno == EAGAIN && (rflags & PMQ_TRY)) return Qnil; rb_sys_fail("mq_receive"); } rb_str_set_len(buffer, x.received); if (rflags & PMQ_WANTARRAY) return rb_ary_new3(2, buffer, UINT2NUM(x.msg_prio)); return buffer; }
/* * call-seq: * mq << string => mq * * Inserts the given +string+ into the message queue with a * default priority of 0 and no timeout. * * Returns itself so its calls may be chained. This use is only * recommended only for users who expect blocking behavior from * the queue. */ static VALUE send0(VALUE self, VALUE buffer) { struct posix_mq *mq = get(self, 1); struct rw_args x; setup_send_buffer(&x, buffer); x.des = mq->des; x.timeout = NULL; x.msg_prio = 0; retry: WITHOUT_GVL(xsend, &x, RUBY_UBF_IO, 0); if (x.retval < 0) { if (errno == EINTR) goto retry; rb_sys_fail("mq_send"); } return self; }
void * acquire_semaphore_without_gvl(void *p) { WITHOUT_GVL(acquire_semaphore, p, RUBY_UBF_IO, NULL); return NULL; }
// int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); // http://man7.org/linux/man-pages/man2/msgsnd.2.html // // Sends a message to the message queue. VALUE sysvmq_send(int argc, VALUE *argv, VALUE self) { VALUE message; VALUE priority = INT2FIX(1); VALUE flags = INT2FIX(0); sysvmq_blocking_call_t blocking; sysvmq_t* sysv; if (argc > 3 || argc == 0) { rb_raise(rb_eArgError, "Wrong number of arguments (1..3)"); } message = argv[0]; if (argc >= 2) priority = argv[1]; if (argc == 3) flags = argv[2]; TypedData_Get_Struct(self, sysvmq_t, &sysvmq_type, sysv); Check_Type(flags, T_FIXNUM); Check_Type(priority, T_FIXNUM); // TODO: Call to_s on message if it responds to // Attach blocking call parameters to the struct passed to the blocking // function wrapper. blocking.flags = FIX2INT(flags); blocking.size = RSTRING_LEN(message); blocking.sysv = sysv; // See msgrcv(2) wrapper blocking.error = UNINITIALIZED_ERROR; blocking.length = UNINITIALIZED_ERROR; // The buffer can be obtained from `sysvmq_maybe_blocking_send`, instead of // passing it, set it directly on the instance struct. sysv->msgbuf->mtype = FIX2INT(priority); if (blocking.size > sysv->buffer_size) { rb_raise(rb_eArgError, "Size of message is bigger than buffer size."); } // TODO: Can a string copy be avoided? memcpy(sysv->msgbuf->mtext, RSTRING_PTR(message), blocking.size); // Non-blocking call, skip the expensive GVL release/acquire if ((blocking.flags & IPC_NOWAIT) == IPC_NOWAIT) { while(sysvmq_maybe_blocking_send(&blocking) == NULL && blocking.error < 0) { if (errno == EINTR) { continue; } rb_sys_fail("Failed sending message to queue"); } } else { // msgsnd(2) can block waiting for a message, if IPC_NOWAIT is not passed. // We unlock the GVL waiting for the call so other threads (e.g. signal // handling) can continue to work. while (WITHOUT_GVL(sysvmq_maybe_blocking_send, &blocking, RUBY_UBF_IO, NULL) == NULL && blocking.error < 0) { if (errno == EINTR || blocking.error == UNINITIALIZED_ERROR) { continue; } rb_sys_fail("Failed sending message to queue"); } } return message; }