lcm_buf_t * lcm_buf_allocate_data(lcm_buf_queue_t * inbufs_empty, lcm_ringbuf_t **ringbuf) { lcm_buf_t * lcmb = NULL; // first allocate a buffer struct for the packet metadata if (lcm_buf_queue_is_empty(inbufs_empty)) { // allocate additional buffer structs if needed int i; for (i = 0; i < LCM_DEFAULT_RECV_BUFS; i++) { lcm_buf_t * nbuf = (lcm_buf_t *) calloc(1, sizeof(lcm_buf_t)); lcm_buf_enqueue(inbufs_empty, nbuf); } } lcmb = lcm_buf_dequeue(inbufs_empty); assert(lcmb); // allocate space on the ringbuffer for the packet data. // give it the maximum possible size for an unfragmented packet lcmb->buf = lcm_ringbuf_alloc(*ringbuf, LCM_MAX_UNFRAGMENTED_PACKET_SIZE); if (lcmb->buf == NULL) { // ringbuffer is full. allocate a larger ringbuffer // Can't free the old ringbuffer yet because it's in use (i.e., full) // Must wait until later to free it. assert(lcm_ringbuf_used(*ringbuf) > 0); dbg(DBG_LCM, "Orphaning ringbuffer %p\n", *ringbuf); unsigned int old_capacity = lcm_ringbuf_capacity(*ringbuf); unsigned int new_capacity = (unsigned int) (old_capacity * 1.5); // replace the passed in ringbuf with the new one *ringbuf = lcm_ringbuf_new(new_capacity); lcmb->buf = lcm_ringbuf_alloc(*ringbuf, 65536); assert(lcmb->buf); dbg(DBG_LCM, "Allocated new ringbuffer size %u\n", new_capacity); } // save a pointer to the ringbuf, in case it gets replaced by another call lcmb->ringbuf = *ringbuf; // zero the last byte so that strlen never segfaults lcmb->buf[65535] = 0; return lcmb; }
static int _setup_recv_parts (lcm_udpm_t *lcm) { g_static_rec_mutex_lock(&lcm->mutex); // some thread synchronization code to ensure that only one thread sets up the // receive thread, and that all threads entering this function after the thread // setup begins wait for it to finish. if(lcm->creating_read_thread) { // check if this thread is the one creating the receive thread. // If so, just return. if(g_static_private_get(&CREATE_READ_THREAD_PKEY)) { g_static_rec_mutex_unlock(&lcm->mutex); return 0; } // ugly bit with two mutexes because we can't use a GStaticRecMutex with a GCond g_mutex_lock(lcm->create_read_thread_mutex); g_static_rec_mutex_unlock(&lcm->mutex); // wait for the thread creating the read thread to finish while(lcm->creating_read_thread) { g_cond_wait(lcm->create_read_thread_cond, lcm->create_read_thread_mutex); } g_mutex_unlock(lcm->create_read_thread_mutex); g_static_rec_mutex_lock(&lcm->mutex); // if we've gotten here, then either the read thread is created, or it // was not possible to do so. Figure out which happened, and return. int result = lcm->thread_created ? 0 : -1; g_static_rec_mutex_unlock(&lcm->mutex); return result; } else if(lcm->thread_created) { g_static_rec_mutex_unlock(&lcm->mutex); return 0; } // no other thread is trying to create the read thread right now. claim that task. lcm->creating_read_thread = 1; lcm->create_read_thread_mutex = g_mutex_new(); lcm->create_read_thread_cond = g_cond_new(); // mark this thread as the one creating the read thread g_static_private_set(&CREATE_READ_THREAD_PKEY, GINT_TO_POINTER(1), NULL); dbg (DBG_LCM, "allocating resources for receiving messages\n"); // allocate the fragment buffer hashtable lcm->frag_bufs = lcm_frag_buf_store_new(MAX_FRAG_BUF_TOTAL_SIZE, MAX_NUM_FRAG_BUFS); // allocate multicast socket lcm->recvfd = socket (AF_INET, SOCK_DGRAM, 0); if (lcm->recvfd < 0) { perror ("allocating LCM recv socket"); goto setup_recv_thread_fail; } struct sockaddr_in addr; memset (&addr, 0, sizeof (addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = lcm->params.mc_port; // allow other applications on the local machine to also bind to this // multicast address and port int opt=1; dbg (DBG_LCM, "LCM: setting SO_REUSEADDR\n"); if (setsockopt (lcm->recvfd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof (opt)) < 0) { perror ("setsockopt (SOL_SOCKET, SO_REUSEADDR)"); goto setup_recv_thread_fail; } #ifdef USE_REUSEPORT /* Mac OS and FreeBSD require the REUSEPORT option in addition * to REUSEADDR or it won't let multiple processes bind to the * same port, even if they are using multicast. */ dbg (DBG_LCM, "LCM: setting SO_REUSEPORT\n"); if (setsockopt (lcm->recvfd, SOL_SOCKET, SO_REUSEPORT, (char*)&opt, sizeof (opt)) < 0) { perror ("setsockopt (SOL_SOCKET, SO_REUSEPORT)"); goto setup_recv_thread_fail; } #endif #if 0 // set loopback option so that packets sent out on the multicast socket // are also delivered to it unsigned char lo_opt = 1; dbg (DBG_LCM, "LCM: setting multicast loopback option\n"); status = setsockopt (lcm->recvfd, IPPROTO_IP, IP_MULTICAST_LOOP, &lo_opt, sizeof (lo_opt)); if (status < 0) { perror ("setting multicast loopback"); return -1; } #endif #ifdef WIN32 // Windows has small (8k) buffer by default // Increase it to a default reasonable amount int recv_buf_size = 2048 * 1024; setsockopt(lcm->recvfd, SOL_SOCKET, SO_RCVBUF, (char*)&recv_buf_size, sizeof(recv_buf_size)); #endif // debugging... how big is the receive buffer? unsigned int retsize = sizeof (int); getsockopt (lcm->recvfd, SOL_SOCKET, SO_RCVBUF, (char*)&lcm->kernel_rbuf_sz, (socklen_t *) &retsize); dbg (DBG_LCM, "LCM: receive buffer is %d bytes\n", lcm->kernel_rbuf_sz); if (lcm->params.recv_buf_size) { if (setsockopt (lcm->recvfd, SOL_SOCKET, SO_RCVBUF, (char *) &lcm->params.recv_buf_size, sizeof (lcm->params.recv_buf_size)) < 0) { perror ("setsockopt(SOL_SOCKET, SO_RCVBUF)"); fprintf (stderr, "Warning: Unable to set recv buffer size\n"); } getsockopt (lcm->recvfd, SOL_SOCKET, SO_RCVBUF, (char*)&lcm->kernel_rbuf_sz, (socklen_t *) &retsize); dbg (DBG_LCM, "LCM: receive buffer is %d bytes\n", lcm->kernel_rbuf_sz); if (lcm->params.recv_buf_size > lcm->kernel_rbuf_sz) { g_warning ("LCM UDP receive buffer size (%d) \n" " is smaller than reqested (%d). " "For more info:\n" " http://lcm-proj.github.io/multicast_setup.html\n", lcm->kernel_rbuf_sz, lcm->params.recv_buf_size); } } /* Enable per-packet timestamping by the kernel, if available */ #ifdef SO_TIMESTAMP opt = 1; setsockopt (lcm->recvfd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof (opt)); #endif if (bind (lcm->recvfd, (struct sockaddr*)&addr, sizeof (addr)) < 0) { perror ("bind"); goto setup_recv_thread_fail; } struct ip_mreq mreq; mreq.imr_multiaddr = lcm->params.mc_addr; mreq.imr_interface.s_addr = INADDR_ANY; // join the multicast group dbg (DBG_LCM, "LCM: joining multicast group\n"); if (setsockopt (lcm->recvfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof (mreq)) < 0) { perror ("setsockopt (IPPROTO_IP, IP_ADD_MEMBERSHIP)"); goto setup_recv_thread_fail; } lcm->inbufs_empty = lcm_buf_queue_new (); lcm->inbufs_filled = lcm_buf_queue_new (); lcm->ringbuf = lcm_ringbuf_new (LCM_RINGBUF_SIZE); int i; for (i = 0; i < LCM_DEFAULT_RECV_BUFS; i++) { /* We don't set the receive buffer's data pointer yet because it * will be taken from the ringbuffer at receive time. */ lcm_buf_t * lcmb = (lcm_buf_t *) calloc (1, sizeof (lcm_buf_t)); lcm_buf_enqueue (lcm->inbufs_empty, lcmb); } // setup a pipe for notifying the reader thread when to quit if(0 != lcm_internal_pipe_create(lcm->thread_msg_pipe)) { perror(__FILE__ " pipe(setup)"); goto setup_recv_thread_fail; } fcntl (lcm->thread_msg_pipe[1], F_SETFL, O_NONBLOCK); /* Start the reader thread */ lcm->read_thread = g_thread_create (recv_thread, lcm, TRUE, NULL); if (!lcm->read_thread) { fprintf (stderr, "Error: LCM failed to start reader thread\n"); goto setup_recv_thread_fail; } lcm->thread_created = 1; g_static_rec_mutex_unlock(&lcm->mutex); // conduct a self-test just to make sure everything is working. dbg (DBG_LCM, "LCM: conducting self test\n"); int self_test_results = udpm_self_test(lcm); g_static_rec_mutex_lock(&lcm->mutex); if (0 == self_test_results) { dbg (DBG_LCM, "LCM: self test successful\n"); } else { // self test failed. destroy the read thread fprintf (stderr, "LCM self test failed!!\n" "Check your routing tables and firewall settings\n"); _destroy_recv_parts (lcm); } // notify threads waiting for the read thread to be created g_mutex_lock(lcm->create_read_thread_mutex); lcm->creating_read_thread = 0; g_cond_broadcast(lcm->create_read_thread_cond); g_mutex_unlock(lcm->create_read_thread_mutex); g_static_rec_mutex_unlock(&lcm->mutex); return self_test_results; setup_recv_thread_fail: _destroy_recv_parts (lcm); g_static_rec_mutex_unlock(&lcm->mutex); return -1; }
static int _setup_recv_thread (lcm_udpm_t *lcm) { assert (!lcm->thread_created); dbg (DBG_LCM, "allocating resources for receiving messages\n"); // allocate multicast socket lcm->recvfd = socket (AF_INET, SOCK_DGRAM, 0); if (lcm->recvfd < 0) { perror ("allocating LCM recv socket"); _destroy_recv_parts (lcm); return -1; } struct sockaddr_in addr; memset (&addr, 0, sizeof (addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = lcm->params.mc_port; // allow other applications on the local machine to also bind to this // multicast address and port int opt=1; dbg (DBG_LCM, "LCM: setting SO_REUSEADDR\n"); if (setsockopt (lcm->recvfd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof (opt)) < 0) { perror ("setsockopt (SOL_SOCKET, SO_REUSEADDR)"); _destroy_recv_parts (lcm); return -1; } #ifdef USE_REUSEPORT /* Mac OS and FreeBSD require the REUSEPORT option in addition * to REUSEADDR or it won't let multiple processes bind to the * same port, even if they are using multicast. */ dbg (DBG_LCM, "LCM: setting SO_REUSEPORT\n"); if (setsockopt (lcm->recvfd, SOL_SOCKET, SO_REUSEPORT, (char*)&opt, sizeof (opt)) < 0) { perror ("setsockopt (SOL_SOCKET, SO_REUSEPORT)"); _destroy_recv_parts (lcm); return -1; } #endif #if 0 // set loopback option so that packets sent out on the multicast socket // are also delivered to it unsigned char lo_opt = 1; dbg (DBG_LCM, "LCM: setting multicast loopback option\n"); status = setsockopt (lcm->recvfd, IPPROTO_IP, IP_MULTICAST_LOOP, &lo_opt, sizeof (lo_opt)); if (status < 0) { perror ("setting multicast loopback"); return -1; } #endif #ifdef WIN32 // Windows has small (8k) buffer by default // Increase it to a default reasonable amount int recv_buf_size = 2048 * 1024; setsockopt(lcm->recvfd, SOL_SOCKET, SO_RCVBUF, (char*)&recv_buf_size, sizeof(recv_buf_size)); #endif // debugging... how big is the receive buffer? unsigned int retsize = sizeof (int); getsockopt (lcm->recvfd, SOL_SOCKET, SO_RCVBUF, (char*)&lcm->kernel_rbuf_sz, (socklen_t *) &retsize); dbg (DBG_LCM, "LCM: receive buffer is %d bytes\n", lcm->kernel_rbuf_sz); if (lcm->params.recv_buf_size) { if (setsockopt (lcm->recvfd, SOL_SOCKET, SO_RCVBUF, (char *) &lcm->params.recv_buf_size, sizeof (lcm->params.recv_buf_size)) < 0) { perror ("setsockopt(SOL_SOCKET, SO_RCVBUF)"); fprintf (stderr, "Warning: Unable to set recv buffer size\n"); } getsockopt (lcm->recvfd, SOL_SOCKET, SO_RCVBUF, (char*)&lcm->kernel_rbuf_sz, (socklen_t *) &retsize); dbg (DBG_LCM, "LCM: receive buffer is %d bytes\n", lcm->kernel_rbuf_sz); if (lcm->params.recv_buf_size > lcm->kernel_rbuf_sz) { g_warning ("LCM UDP receive buffer size (%d) \n" " is smaller than reqested (%d). " "For more info:\n" " http://lcm.googlecode.com/svn/www/reference/lcm/multicast-setup.html\n", lcm->kernel_rbuf_sz, lcm->params.recv_buf_size); } } /* Enable per-packet timestamping by the kernel, if available */ #ifdef SO_TIMESTAMP opt = 1; setsockopt (lcm->recvfd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof (opt)); #endif if (bind (lcm->recvfd, (struct sockaddr*)&addr, sizeof (addr)) < 0) { perror ("bind"); _destroy_recv_parts (lcm); return -1; } struct ip_mreq mreq; mreq.imr_multiaddr = lcm->params.mc_addr; mreq.imr_interface.s_addr = INADDR_ANY; // join the multicast group dbg (DBG_LCM, "LCM: joining multicast group\n"); if (setsockopt (lcm->recvfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof (mreq)) < 0) { perror ("setsockopt (IPPROTO_IP, IP_ADD_MEMBERSHIP)"); _destroy_recv_parts (lcm); return -1; } lcm->inbufs_empty = lcm_buf_queue_new (); lcm->inbufs_filled = lcm_buf_queue_new (); lcm->ringbuf = lcm_ringbuf_new (LCM_RINGBUF_SIZE); int i; for (i = 0; i < LCM_DEFAULT_RECV_BUFS; i++) { /* We don't set the receive buffer's data pointer yet because it * will be taken from the ringbuffer at receive time. */ lcm_buf_t * lcmb = (lcm_buf_t *) calloc (1, sizeof (lcm_buf_t)); lcm_buf_enqueue (lcm->inbufs_empty, lcmb); } // setup a pipe for notifying the reader thread when to quit if(0 != lcm_internal_pipe_create(lcm->thread_msg_pipe)) { perror(__FILE__ " pipe(setup)"); _destroy_recv_parts (lcm); return -1; } fcntl (lcm->thread_msg_pipe[1], F_SETFL, O_NONBLOCK); /* Start the reader thread */ lcm->read_thread = g_thread_create (recv_thread, lcm, TRUE, NULL); if (!lcm->read_thread) { fprintf (stderr, "Error: LCM failed to start reader thread\n"); _destroy_recv_parts (lcm); return -1; } lcm->thread_created = 1; dbg (DBG_LCM, "LCM: conducting self test\n"); if (udpm_self_test (lcm) < 0) { fprintf (stderr, "LCM self test failed!!\n" "Check your routing tables and firewall settings\n"); _destroy_recv_parts (lcm); return -1; } dbg (DBG_LCM, "LCM: self test successful\n"); return 0; }