void insert_probe_timer_entry( probe_timer_entry *new_entry ) { probe_timer_entry *last_entry = probe_timer_last->data; if ( probe_timer_table->next == NULL || timespec_le( &(last_entry->expires ), &( new_entry->expires ) ) ) { probe_timer_last = insert_after_dlist( probe_timer_last, new_entry ); goto set_interval_timer; } dlist_element *dlist; probe_timer_entry *entry; for ( dlist = probe_timer_table->next; dlist != NULL; dlist = dlist->next ) { entry = dlist->data; if ( timespec_le( &(entry->expires ), &( new_entry->expires ) ) ) { continue; } insert_before_dlist( dlist, new_entry ); goto set_interval_timer; } UNREACHABLE(); set_interval_timer: if ( probe_timer_table->next->data != new_entry ) { return; } #if 0 set_interval_timer(); #endif }
static void interval_timer_event( void *user_data ) { UNUSED( user_data ); struct timespec now; dlist_element *dlist, *next; probe_timer_entry *entry; for ( dlist = probe_timer_table->next; dlist != NULL; dlist = next ) { next = dlist->next; entry = dlist->data; get_current_time( &now ); if ( timespec_le( &( entry->expires ), &now ) ) { if ( dlist == probe_timer_last ) { probe_timer_last = dlist->prev; } delete_dlist_element( dlist ); probe_request( entry, PROBE_TIMER_EVENT_TIMEOUT, 0, 0 ); } else { #if 0 set_interval_timer(); #endif return; } } }
static inline bool other_port_status_changed( probe_timer_entry *entry ) { if ( entry->link_up ) { probe_timer_entry *peer = lookup_probe_timer_entry( &entry->to_datapath_id, entry->to_port_no ); return peer == NULL; } return !timespec_le( &port_down_time, &( entry->link_down_time ) ); }
int refresh_file( struct file_state *f_s ){ struct timespec now, tmp; int i = 0; if (f_s->datap) { /* get time */ if ( clock_gettime(CLOCK_MONOTONIC, &now) < 0 ) { /* if we don't know what time it is */ /* there's nothing we can do with this */ return 0; } /* if time since last refresh > delta */ tmp = timespec_add( &f_s->ts, &file_time_offset); if ( timespec_le( &now, &tmp) ) { /* file is recent */ return 0; } f_s->ts = now; /* clear old data, make errors obvious, autoterm trailing strings */ memset ( f_s->datap, 0, f_s->datas ); } /* if fd is null open file */ if ( f_s->fd <= 0) { if (( f_s->fd = open (f_s->filename, O_RDONLY)) < 0) { perror("refresh_file: open"); return -1; } } if ( lseek (f_s->fd, 0, SEEK_SET) < 0 ) { perror("refresh_file: initial seek"); return -1; } /* only grow, never shrink... what would be the point? */ while (f_s->datap && (i = read (f_s->fd, f_s->datap, f_s->datas)) >= f_s->datas ){ /* oh heck, what do I do if this fails? */ f_s->datas += BUFFERBLOCK; if ((f_s->datap = realloc(f_s->datap, f_s->datas)) == NULL){ free((char *)f_s->datap); perror("refresh_file: realloc"); return -1; } if ( lseek (f_s->fd, 0, SEEK_SET) < 0 ) { perror("refresh_file: subsequent seek"); return -1; } } if (i < 0 ){ /* read failed */ perror("refresh_file: file read failed"); return -1; } return 0; }
static void event_loop(struct server_state* ss) { msg(1, "Starting event loop\n"); struct timespec tx_ts, rx_ts, next_tx_ts, lost_tx_ts = { 0, 0 }; ss->have_sent = ss->have_tx_ts = ss->have_rx_ts = false; int rc, rx_left = ss->rx_msg_size; unsigned send_i = 0; clock_gettime(CLOCK_REALTIME, &next_tx_ts); timespec_add_ns(&next_tx_ts, ss->inter_tx_gap_ns); while( 1 ) { struct epoll_event e; TRY( rc = epoll_wait(ss->epoll, &e, 1, 0) ); if( rc == 0 ) { struct timespec now; clock_gettime(CLOCK_REALTIME, &now); if( ! timespec_le(next_tx_ts, now) ) continue; timespec_add_ns(&next_tx_ts, ss->inter_tx_gap_ns); if( ++send_i >= cfg_measure_nth && ! ss->have_sent ) { msg(3, "Send message (timed)\n"); TEST( send(ss->udp_sock_ts, ss->tx_buf_ts, ss->tx_msg_size, 0) == ss->tx_msg_size ); if( ! cfg_hw_ts ) { ss->have_tx_ts = true; tx_ts = now; } send_i = 0; ss->have_sent = true; } else { msg(3, "Send message\n"); TEST( send(ss->udp_sock, ss->tx_buf, ss->tx_msg_size, 0) == ss->tx_msg_size ); if( send_i >= cfg_measure_nth ) { /* Not had a reply to last timed message. Try to detect lost * messages. */ if( send_i == cfg_measure_nth ) { lost_tx_ts = now; } else if( ss->have_tx_ts && timespec_diff_ns(now, lost_tx_ts) > 10000000 ) { msg(2, "WARNING: No response to timed message\n"); if( ss->rtt_n > 0 ) ++(ss->n_lost_msgs); ss->have_sent = false; ss->have_tx_ts = false; ss->have_rx_ts = false; } } } } else if( e.data.fd == ss->tcp_sock ) { TEST( e.events & EPOLLIN ); if( cfg_hw_ts ) { rc = recv_ts(ss->tcp_sock, ss->rx_buf, rx_left, MSG_DONTWAIT, &rx_ts); } else { rc = recv(ss->tcp_sock, ss->rx_buf, rx_left, MSG_DONTWAIT); clock_gettime(CLOCK_REALTIME, &rx_ts); } if( rc > 0 ) { msg(3, "Received %d from client at %d.%09d\n", rc, (int) rx_ts.tv_sec, (int) rx_ts.tv_nsec); if( (rx_left -= rc) == 0 ) { send(ss->tcp_sock, ss->rx_buf, 1, MSG_NOSIGNAL); rx_left = ss->rx_msg_size; ss->have_rx_ts = true; if( ss->have_tx_ts ) measured_rtt(ss, tx_ts, rx_ts); } } else if( rc == 0 || errno == ECONNRESET ) { break; } else if( errno == ETIME ) { fprintf(stderr, "ERROR: Did not get H/W timestamp on RX\n"); exit(3); } else { TRY( rc ); } } else if( e.data.fd == ss->udp_sock_ts ) { assert( cfg_hw_ts ); assert( ! ss->have_tx_ts ); TEST( recv_ts(ss->udp_sock_ts, ss->rx_buf, 1, MSG_ERRQUEUE | MSG_DONTWAIT, &tx_ts) == 1 ); msg(3, "TX timestamp %d.%09d\n", (int) tx_ts.tv_sec, (int) tx_ts.tv_nsec); ss->have_tx_ts = true; if( ss->have_rx_ts ) measured_rtt(ss, tx_ts, rx_ts); } } msg(1, "Client disconnected\n"); TRY( close(ss->tcp_sock) ); }
int sys_ppoll(struct pollfd* user_fds, size_t nfds, const struct timespec* user_timeout_ts, const sigset_t* user_sigmask) { ioctx_t ctx; SetupKernelIOCtx(&ctx); struct timespec timeout_ts; if ( !FetchTimespec(&timeout_ts, user_timeout_ts) ) return -1; if ( user_sigmask ) return errno = ENOSYS, -1; struct pollfd* fds = CopyFdsFromUser(user_fds, nfds); if ( !fds ) { return -1; } PollNode* nodes = new PollNode[nfds]; if ( !nodes ) { delete[] fds; return -1; } Process* process = CurrentProcess(); kthread_mutex_t wakeup_mutex = KTHREAD_MUTEX_INITIALIZER; kthread_cond_t wakeup_cond = KTHREAD_COND_INITIALIZER; kthread_mutex_lock(&wakeup_mutex); int ret = -1; bool self_woken = false; bool remote_woken = false; bool unexpected_error = false; Timer timer; struct poll_timeout pts; if ( timespec_le(timespec_make(0, 1), timeout_ts) ) { timer.Attach(Time::GetClock(CLOCK_MONOTONIC)); struct itimerspec its; its.it_interval = timespec_nul(); its.it_value = timeout_ts; pts.wake_mutex = &wakeup_mutex; pts.wake_cond = &wakeup_cond; pts.woken = &remote_woken; timer.Set(&its, NULL, 0, poll_timeout_callback, &pts); } size_t reqs; for ( reqs = 0; !unexpected_error && reqs < nfds; ) { PollNode* node = nodes + reqs; if ( fds[reqs].fd < 0 ) { fds[reqs].revents = POLLNVAL; // TODO: Should we set POLLNVAL in node->revents too? Should this // system call ignore this error and keep polling, or return to // user-space immediately? What if conditions are already true on // some of the file descriptors (those we have processed so far?)? node->revents = 0; reqs++; continue; } Ref<Descriptor> desc = process->GetDescriptor(fds[reqs].fd); if ( !desc ) { self_woken = unexpected_error = true; break; } node->events = fds[reqs].events | POLL__ONLY_REVENTS; node->revents = 0; node->wake_mutex = &wakeup_mutex; node->wake_cond = &wakeup_cond; node->woken = &remote_woken; reqs++; // TODO: How should errors be handled? if ( desc->poll(&ctx, node) == 0 ) self_woken = true; else if ( errno == EAGAIN ) errno = 0; else unexpected_error = self_woken = true; } if ( timeout_ts.tv_sec == 0 && timeout_ts.tv_nsec == 0 ) self_woken = true; while ( !(self_woken || remote_woken) ) { if ( !kthread_cond_wait_signal(&wakeup_cond, &wakeup_mutex) ) errno = -EINTR, self_woken = true; } kthread_mutex_unlock(&wakeup_mutex); for ( size_t i = 0; i < reqs; i++ ) if ( 0 <= fds[i].fd ) nodes[i].Cancel(); if ( timespec_le(timespec_make(0, 1), timeout_ts) ) { timer.Cancel(); timer.Detach(); } if ( !unexpected_error ) { int num_events = 0; for ( size_t i = 0; i < reqs; i++ ) { if ( fds[i].fd < -1 ) continue; if ( (fds[i].revents = nodes[i].revents) ) num_events++; } if ( CopyFdsToUser(user_fds, fds, nfds) ) ret = num_events; } delete[] nodes; delete[] fds; return ret; }