/* Scan the closing list, and put pending closing keys to free list. * Must do this with ioqueue mutex held. */ static void scan_closing_keys(pj_ioqueue_t *ioqueue) { if (!pj_list_empty(&ioqueue->closing_list)) { pj_time_val now; pj_ioqueue_key_t *key; pj_gettickcount(&now); /* Move closing keys to free list when they've finished the closing * idle time. */ key = ioqueue->closing_list.next; while (key != &ioqueue->closing_list) { pj_ioqueue_key_t *next = key->next; pj_assert(key->closing != 0); if (PJ_TIME_VAL_GTE(now, key->free_time)) { pj_list_erase(key); pj_list_push_back(&ioqueue->free_list, key); } key = next; } } }
void poll_events(pj_stun_config *stun_cfg, unsigned msec, pj_bool_t first_event_only) { pj_time_val stop_time; int count = 0; pj_gettimeofday(&stop_time); stop_time.msec += msec; pj_time_val_normalize(&stop_time); /* Process all events for the specified duration. */ for (;;) { pj_time_val timeout = {0, 1}, now; int c; c = pj_timer_heap_poll( stun_cfg->timer_heap, NULL ); if (c > 0) count += c; //timeout.sec = timeout.msec = 0; c = pj_ioqueue_poll( stun_cfg->ioqueue, &timeout); if (c > 0) count += c; pj_gettimeofday(&now); if (PJ_TIME_VAL_GTE(now, stop_time)) break; if (first_event_only && count >= 0) break; } }
static void on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { pj_ssize_t size; char *sendbuf = "Hello world"; pj_status_t status; if (sock_data.unregistered) return; pj_mutex_lock(sock_data.mutex); if (sock_data.unregistered) { pj_mutex_unlock(sock_data.mutex); return; } if (bytes_read < 0) { if (-bytes_read != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) app_perror("ioqueue reported recv error", -bytes_read); } else { sock_data.received += bytes_read; } if (test_method == UNREGISTER_IN_CALLBACK) { pj_time_val now; pj_gettimeofday(&now); if (PJ_TIME_VAL_GTE(now, time_to_unregister)) { sock_data.unregistered = 1; pj_ioqueue_unregister(key); pj_mutex_destroy(sock_data.mutex); pj_pool_release(sock_data.pool); sock_data.pool = NULL; return; } } do { size = sock_data.bufsize; status = pj_ioqueue_recv(key, op_key, sock_data.buffer, &size, 0); if (status != PJ_EPENDING && status != PJ_SUCCESS) app_perror("recv() error", status); } while (status == PJ_SUCCESS); pj_mutex_unlock(sock_data.mutex); size = pj_ansi_strlen(sendbuf); status = pj_sock_send(sock_data.csock, sendbuf, &size, 0); if (status != PJ_SUCCESS) app_perror("send() error", status); size = pj_ansi_strlen(sendbuf); status = pj_sock_send(sock_data.csock, sendbuf, &size, 0); if (status != PJ_SUCCESS) app_perror("send() error", status); }
static int server_thread(void *arg) { pj_time_val timeout = { 0, 1 }; unsigned thread_index = (unsigned)(long)arg; pj_time_val last_report, next_report; pj_gettimeofday(&last_report); next_report = last_report; next_report.sec++; while (!app.thread_quit) { pj_time_val now; unsigned i; for (i=0; i<100; ++i) { unsigned count = 0; pjsip_endpt_handle_events2(app.sip_endpt, &timeout, &count); if (count == 0) break; } if (thread_index == 0) { pj_gettimeofday(&now); if (PJ_TIME_VAL_GTE(now, next_report)) { pj_time_val tmp; unsigned msec; unsigned stateless, stateful, call; char str_stateless[32], str_stateful[32], str_call[32]; tmp = now; PJ_TIME_VAL_SUB(tmp, last_report); msec = PJ_TIME_VAL_MSEC(tmp); last_report = now; next_report = last_report; next_report.sec++; stateless = app.server.cur_state.stateless_cnt - app.server.prev_state.stateless_cnt; stateful = app.server.cur_state.stateful_cnt - app.server.prev_state.stateful_cnt; call = app.server.cur_state.call_cnt - app.server.prev_state.call_cnt; good_number(str_stateless, app.server.cur_state.stateless_cnt); good_number(str_stateful, app.server.cur_state.stateful_cnt); good_number(str_call, app.server.cur_state.call_cnt); printf("Total(rate): stateless:%s (%d/s), statefull:%s (%d/s), call:%s (%d/s) \r", str_stateless, stateless*1000/msec, str_stateful, stateful*1000/msec, str_call, call*1000/msec); fflush(stdout); app.server.prev_state = app.server.cur_state; } } } return 0; }
/* Scan closing keys to be put to free list again */ static void scan_closing_keys(pj_ioqueue_t *ioqueue) { pj_time_val now; pj_ioqueue_key_t *h; pj_gettimeofday(&now); h = ioqueue->closing_list.next; while (h != &ioqueue->closing_list) { pj_ioqueue_key_t *next = h->next; pj_assert(h->closing != 0); if (PJ_TIME_VAL_GTE(now, h->free_time)) { pj_list_erase(h); pj_list_push_back(&ioqueue->free_list, h); } h = next; } }
void flush_events(unsigned duration) { pj_time_val stop_time; pj_gettimeofday(&stop_time); stop_time.msec += duration; pj_time_val_normalize(&stop_time); /* Process all events for the specified duration. */ for (;;) { pj_time_val timeout = {0, 1}, now; pjsip_endpt_handle_events(endpt, &timeout); pj_gettimeofday(&now); if (PJ_TIME_VAL_GTE(now, stop_time)) break; } }
/* Scan closing keys to be put to free list again */ static void scan_closing_keys(pj_ioqueue_t *ioqueue) { pj_time_val now; pj_ioqueue_key_t *h; pj_gettickcount(&now); h = ioqueue->closing_list.next; while (h != &ioqueue->closing_list) { pj_ioqueue_key_t *next = h->next; pj_assert(h->closing != 0); if (PJ_TIME_VAL_GTE(now, h->free_time)) { pj_list_erase(h); // Don't set grp_lock to NULL otherwise the other thread // will crash. Just leave it as dangling pointer, but this // should be safe //h->grp_lock = NULL; pj_list_push_back(&ioqueue->free_list, h); } h = next; } }
static pj_timer_entry * remove_node( pj_timer_heap_t *ht, size_t slot) { pj_timer_entry *removed_node = ht->heap[slot]; // Return this timer id to the freelist. push_freelist( ht, removed_node->_timer_id ); // Decrement the size of the heap by one since we're removing the // "slot"th node. ht->cur_size--; // Set the ID removed_node->_timer_id = -1; // Only try to reheapify if we're not deleting the last entry. if (slot < ht->cur_size) { int parent; pj_timer_entry *moved_node = ht->heap[ht->cur_size]; // Move the end node to the location being removed and update // the corresponding slot in the parallel <timer_ids> array. copy_node( ht, slot, moved_node); // If the <moved_node->time_value_> is great than or equal its // parent it needs be moved down the heap. parent = HEAP_PARENT (slot); if (PJ_TIME_VAL_GTE(moved_node->_timer_value, ht->heap[parent]->_timer_value)) reheap_down( ht, moved_node, slot, HEAP_LEFT(slot)); else reheap_up( ht, moved_node, slot, parent); } return removed_node; }
static int file_test_internal(void) { enum { FILE_MAX_AGE = 1000 }; pj_oshandle_t fd = 0; pj_status_t status; char readbuf[sizeof(buffer)+16]; pj_file_stat stat; pj_time_val start_time; pj_ssize_t size; pj_off_t pos; PJ_LOG(3,("", "..file io test..")); /* Get time. */ pj_gettimeofday(&start_time); /* Delete original file if exists. */ if (pj_file_exists(FILENAME)) pj_file_delete(FILENAME); /* * Write data to the file. */ status = pj_file_open(NULL, FILENAME, PJ_O_WRONLY, &fd); if (status != PJ_SUCCESS) { app_perror("...file_open() error", status); return -10; } size = sizeof(buffer); status = pj_file_write(fd, buffer, &size); if (status != PJ_SUCCESS) { app_perror("...file_write() error", status); pj_file_close(fd); return -20; } if (size != sizeof(buffer)) return -25; status = pj_file_close(fd); if (status != PJ_SUCCESS) { app_perror("...file_close() error", status); return -30; } /* Check the file existance and size. */ if (!pj_file_exists(FILENAME)) return -40; if (pj_file_size(FILENAME) != sizeof(buffer)) return -50; /* Get file stat. */ status = pj_file_getstat(FILENAME, &stat); if (status != PJ_SUCCESS) return -60; /* Check stat size. */ if (stat.size != sizeof(buffer)) return -70; #if INCLUDE_FILE_TIME_TEST /* Check file creation time >= start_time. */ if (!PJ_TIME_VAL_GTE(stat.ctime, start_time)) return -80; /* Check file creation time is not much later. */ PJ_TIME_VAL_SUB(stat.ctime, start_time); if (stat.ctime.sec > FILE_MAX_AGE) return -90; /* Check file modification time >= start_time. */ if (!PJ_TIME_VAL_GTE(stat.mtime, start_time)) return -80; /* Check file modification time is not much later. */ PJ_TIME_VAL_SUB(stat.mtime, start_time); if (stat.mtime.sec > FILE_MAX_AGE) return -90; /* Check file access time >= start_time. */ if (!PJ_TIME_VAL_GTE(stat.atime, start_time)) return -80; /* Check file access time is not much later. */ PJ_TIME_VAL_SUB(stat.atime, start_time); if (stat.atime.sec > FILE_MAX_AGE) return -90; #endif /* * Re-open the file and read data. */ status = pj_file_open(NULL, FILENAME, PJ_O_RDONLY, &fd); if (status != PJ_SUCCESS) { app_perror("...file_open() error", status); return -100; } size = 0; while (size < (pj_ssize_t)sizeof(readbuf)) { pj_ssize_t read; read = 1; status = pj_file_read(fd, &readbuf[size], &read); if (status != PJ_SUCCESS) { PJ_LOG(3,("", "...error reading file after %d bytes (error follows)", size)); app_perror("...error", status); return -110; } if (read == 0) { // EOF break; } size += read; } if (size != sizeof(buffer)) return -120; /* if (!pj_file_eof(fd, PJ_O_RDONLY)) return -130; */ if (pj_memcmp(readbuf, buffer, size) != 0) return -140; /* Seek test. */ status = pj_file_setpos(fd, 4, PJ_SEEK_SET); if (status != PJ_SUCCESS) { app_perror("...file_setpos() error", status); return -141; } /* getpos test. */ status = pj_file_getpos(fd, &pos); if (status != PJ_SUCCESS) { app_perror("...file_getpos() error", status); return -142; } if (pos != 4) return -143; status = pj_file_close(fd); if (status != PJ_SUCCESS) { app_perror("...file_close() error", status); return -150; } /* * Rename test. */ status = pj_file_move(FILENAME, NEWNAME); if (status != PJ_SUCCESS) { app_perror("...file_move() error", status); return -160; } if (pj_file_exists(FILENAME)) return -170; if (!pj_file_exists(NEWNAME)) return -180; if (pj_file_size(NEWNAME) != sizeof(buffer)) return -190; /* Delete test. */ status = pj_file_delete(NEWNAME); if (status != PJ_SUCCESS) { app_perror("...file_delete() error", status); return -200; } if (pj_file_exists(NEWNAME)) return -210; PJ_LOG(3,("", "...success")); return PJ_SUCCESS; }
/* This is the RX "tick". * This function is called periodically every "tick" milliseconds, and * it will determine whether to call get_frame() from the RX stream. */ static void rx_tick(const pj_time_val *t) { struct stream *strm = g_app.rx; pjmedia_port *port = g_app.rx->port; long pkt_interval; pkt_interval = PJMEDIA_PIA_SPF(&port->info) * 1000 / PJMEDIA_PIA_SRATE(&port->info) * g_app.cfg.rx_snd_burst; if (PJ_TIME_VAL_GTE(*t, strm->state.rx.next_schedule)) { unsigned i; for (i=0; i<g_app.cfg.rx_snd_burst; ++i) { struct log_entry entry; pjmedia_rtcp_stat stat; pjmedia_jb_state jstate; pj_bool_t has_frame; char msg[120]; unsigned last_empty; pjmedia_stream_get_stat(g_app.rx->strm, &stat); pjmedia_stream_get_stat_jbuf(g_app.rx->strm, &jstate); last_empty = jstate.empty; /* Pre GET event */ pj_bzero(&entry, sizeof(entry)); entry.event = EVENT_GET_PRE; entry.wall_clock = *t; entry.stat = &stat; entry.jb_state = &jstate; write_log(&entry, PJ_TRUE); /* GET */ run_one_frame(g_app.rx->port, g_app.rx_wav, &has_frame); /* Post GET event */ pjmedia_stream_get_stat(g_app.rx->strm, &stat); pjmedia_stream_get_stat_jbuf(g_app.rx->strm, &jstate); pj_bzero(&entry, sizeof(entry)); entry.event = EVENT_GET_POST; entry.wall_clock = *t; entry.stat = &stat; entry.jb_state = &jstate; msg[0] = '\0'; entry.log = msg; if (jstate.empty > last_empty) strcat(msg, "** JBUF was empty **"); if (!has_frame) strcat(msg, "** NULL frame was returned **"); write_log(&entry, PJ_TRUE); } strm->state.rx.next_schedule.msec += pkt_interval; pj_time_val_normalize(&strm->state.rx.next_schedule); } }
/* * Perform unregistration test. * * This will create ioqueue and register a server socket. Depending * on the test method, either the callback or the main thread will * unregister and destroy the server socket after some period of time. */ static int perform_unreg_test(pj_ioqueue_t *ioqueue, pj_pool_t *test_pool, const char *title, pj_bool_t other_socket) { enum { WORKER_CNT = 1, MSEC = 500, QUIT_MSEC = 500 }; int i; pj_thread_t *thread[WORKER_CNT]; struct sock_data osd; pj_ioqueue_callback callback; pj_time_val end_time; pj_status_t status; /* Sometimes its important to have other sockets registered to * the ioqueue, because when no sockets are registered, the ioqueue * will return from the poll early. */ if (other_socket) { status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, 56127, &osd.sock); if (status != PJ_SUCCESS) { app_perror("Error creating other socket", status); return -12; } pj_bzero(&callback, sizeof(callback)); status = pj_ioqueue_register_sock(test_pool, ioqueue, osd.sock, NULL, &callback, &osd.key); if (status != PJ_SUCCESS) { app_perror("Error registering other socket", status); return -13; } } else { osd.key = NULL; osd.sock = PJ_INVALID_SOCKET; } /* Init both time duration of testing */ thread_quitting = 0; pj_gettimeofday(&time_to_unregister); time_to_unregister.msec += MSEC; pj_time_val_normalize(&time_to_unregister); end_time = time_to_unregister; end_time.msec += QUIT_MSEC; pj_time_val_normalize(&end_time); /* Create polling thread */ for (i=0; i<WORKER_CNT; ++i) { status = pj_thread_create(test_pool, "unregtest", &worker_thread, ioqueue, 0, 0, &thread[i]); if (status != PJ_SUCCESS) { app_perror("Error creating thread", status); return -20; } } /* Create pair of client/server sockets */ status = app_socketpair(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock_data.sock, &sock_data.csock); if (status != PJ_SUCCESS) { app_perror("app_socketpair error", status); return -30; } /* Initialize test data */ sock_data.pool = pj_pool_create(mem, "sd", 1000, 1000, NULL); sock_data.buffer = (char*) pj_pool_alloc(sock_data.pool, 128); sock_data.bufsize = 128; sock_data.op_key = (pj_ioqueue_op_key_t*) pj_pool_alloc(sock_data.pool, sizeof(*sock_data.op_key)); sock_data.received = 0; sock_data.unregistered = 0; pj_ioqueue_op_key_init(sock_data.op_key, sizeof(*sock_data.op_key)); status = pj_mutex_create_simple(sock_data.pool, "sd", &sock_data.mutex); if (status != PJ_SUCCESS) { app_perror("create_mutex() error", status); return -35; } /* Register socket to ioqueue */ pj_bzero(&callback, sizeof(callback)); callback.on_read_complete = &on_read_complete; status = pj_ioqueue_register_sock(sock_data.pool, ioqueue, sock_data.sock, NULL, &callback, &sock_data.key); if (status != PJ_SUCCESS) { app_perror("pj_ioqueue_register error", status); return -40; } /* Bootstrap the first send/receive */ on_read_complete(sock_data.key, sock_data.op_key, 0); /* Loop until test time ends */ for (;;) { pj_time_val now, timeout; pj_gettimeofday(&now); if (test_method == UNREGISTER_IN_APP && PJ_TIME_VAL_GTE(now, time_to_unregister) && sock_data.pool) { pj_mutex_lock(sock_data.mutex); sock_data.unregistered = 1; pj_ioqueue_unregister(sock_data.key); pj_mutex_unlock(sock_data.mutex); pj_mutex_destroy(sock_data.mutex); pj_pool_release(sock_data.pool); sock_data.pool = NULL; } if (PJ_TIME_VAL_GT(now, end_time) && sock_data.unregistered) break; timeout.sec = 0; timeout.msec = 10; pj_ioqueue_poll(ioqueue, &timeout); //pj_thread_sleep(1); } thread_quitting = 1; for (i=0; i<WORKER_CNT; ++i) { pj_thread_join(thread[i]); pj_thread_destroy(thread[i]); } if (other_socket) { pj_ioqueue_unregister(osd.key); } pj_sock_close(sock_data.csock); PJ_LOG(3,(THIS_FILE, "....%s: done (%d KB/s)", title, sock_data.received * 1000 / MSEC / 1000)); return 0; }
/* Worker thread for loop transport. */ static int loop_transport_worker_thread(void *arg) { struct loop_transport *loop = arg; struct recv_list r; struct send_list s; pj_list_init(&r); pj_list_init(&s); while (!loop->thread_quit_flag) { pj_time_val now; pj_thread_sleep(1); pj_gettimeofday(&now); pj_lock_acquire(loop->base.lock); /* Move expired send notification to local list. */ while (!pj_list_empty(&loop->send_list)) { struct send_list *node = loop->send_list.next; /* Break when next node time is greater than now. */ if (PJ_TIME_VAL_GTE(node->sent_time, now)) break; /* Delete this from the list. */ pj_list_erase(node); /* Add to local list. */ pj_list_push_back(&s, node); } /* Move expired "incoming" packet to local list. */ while (!pj_list_empty(&loop->recv_list)) { struct recv_list *node = loop->recv_list.next; /* Break when next node time is greater than now. */ if (PJ_TIME_VAL_GTE(node->rdata.pkt_info.timestamp, now)) break; /* Delete this from the list. */ pj_list_erase(node); /* Add to local list. */ pj_list_push_back(&r, node); } pj_lock_release(loop->base.lock); /* Process send notification and incoming packet notification * without holding down the loop's mutex. */ while (!pj_list_empty(&s)) { struct send_list *node = s.next; pj_list_erase(node); /* Notify callback. */ if (node->callback) { (*node->callback)(&loop->base, node->token, node->sent); } /* Decrement tdata reference counter. */ pjsip_tx_data_dec_ref(node->tdata); } /* Process "incoming" packet. */ while (!pj_list_empty(&r)) { struct recv_list *node = r.next; pj_ssize_t size_eaten; pj_list_erase(node); /* Notify transport manager about the "incoming packet" */ size_eaten = pjsip_tpmgr_receive_packet(loop->base.tpmgr, &node->rdata); /* Must "eat" all the packets. */ pj_assert(size_eaten == node->rdata.pkt_info.len); /* Done. */ pjsip_endpt_release_pool(loop->base.endpt, node->rdata.tp_info.pool); } } return 0; }
/* * The generic test framework, used by most of the tests. */ static int perform_test( char *target_uri, char *from_uri, char *branch_param, int test_time, const pjsip_method *method, int request_cnt, int request_interval_msec, int expecting_timeout) { pjsip_tx_data *tdata; pj_str_t target, from; pjsip_via_hdr *via; pj_time_val timeout, next_send; int sent_cnt; pj_status_t status; PJ_LOG(3,(THIS_FILE, " please standby, this will take at most %d seconds..", test_time)); /* Reset test. */ recv_count = 0; test_complete = 0; tsx_key.slen = 0; /* Init headers. */ target = pj_str(target_uri); from = pj_str(from_uri); /* Create request. */ status = pjsip_endpt_create_request( endpt, method, &target, &from, &target, NULL, NULL, -1, NULL, &tdata); if (status != PJ_SUCCESS) { app_perror(" Error: unable to create request", status); return -10; } /* Set the branch param for test 1. */ via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); via->branch_param = pj_str(branch_param); /* Schedule first send. */ sent_cnt = 0; pj_gettimeofday(&next_send); pj_time_val_normalize(&next_send); /* Set test completion time. */ pj_gettimeofday(&timeout); timeout.sec += test_time; /* Wait until test complete. */ while (!test_complete) { pj_time_val now, poll_delay = {0, 10}; pjsip_endpt_handle_events(endpt, &poll_delay); pj_gettimeofday(&now); if (sent_cnt < request_cnt && PJ_TIME_VAL_GTE(now, next_send)) { /* Add additional reference to tdata to prevent transaction from * deleting it. */ pjsip_tx_data_add_ref(tdata); /* (Re)Send the request. */ PJ_LOG(4,(THIS_FILE, " (re)sending request %d", sent_cnt)); status = pjsip_endpt_send_request_stateless(endpt, tdata, 0, 0); if (status != PJ_SUCCESS) { app_perror(" Error: unable to send request", status); pjsip_tx_data_dec_ref(tdata); return -20; } /* Schedule next send, if any. */ sent_cnt++; if (sent_cnt < request_cnt) { pj_gettimeofday(&next_send); next_send.msec += request_interval_msec; pj_time_val_normalize(&next_send); } } if (now.sec > timeout.sec) { if (!expecting_timeout) PJ_LOG(3,(THIS_FILE, " Error: test has timed out")); pjsip_tx_data_dec_ref(tdata); return TEST_TIMEOUT_ERROR; } } if (test_complete < 0) { pjsip_transaction *tsx; tsx = pjsip_tsx_layer_find_tsx(&tsx_key, PJ_TRUE); if (tsx) { pjsip_tsx_terminate(tsx, PJSIP_SC_REQUEST_TERMINATED); pj_mutex_unlock(tsx->mutex); flush_events(1000); } pjsip_tx_data_dec_ref(tdata); return test_complete; } /* Allow transaction to destroy itself */ flush_events(500); /* Make sure transaction has been destroyed. */ if (pjsip_tsx_layer_find_tsx(&tsx_key, PJ_FALSE) != NULL) { PJ_LOG(3,(THIS_FILE, " Error: transaction has not been destroyed")); pjsip_tx_data_dec_ref(tdata); return -40; } /* Check tdata reference counter. */ if (pj_atomic_get(tdata->ref_cnt) != 1) { PJ_LOG(3,(THIS_FILE, " Error: tdata reference counter is %d", pj_atomic_get(tdata->ref_cnt))); pjsip_tx_data_dec_ref(tdata); return -50; } /* Destroy txdata */ pjsip_tx_data_dec_ref(tdata); return PJ_SUCCESS; }
/* Client worker thread */ static int client_thread(void *arg) { pj_time_val end_time, last_report, now; unsigned thread_index = (unsigned)(long)arg; unsigned cycle = 0, last_cycle = 0; pj_thread_sleep(100); pj_gettimeofday(&end_time); end_time.sec += app.client.timeout; pj_gettimeofday(&last_report); if (app.client.first_request.sec == 0) { pj_gettimeofday(&app.client.first_request); } /* Submit all jobs */ while (app.client.job_submitted < app.client.job_count && !app.thread_quit){ pj_time_val timeout = { 0, 1 }; unsigned i; int outstanding; pj_status_t status; /* Calculate current outstanding job */ outstanding = app.client.job_submitted - app.client.job_finished; /* Update stats on max outstanding jobs */ if (outstanding > (int)app.client.stat_max_window) app.client.stat_max_window = outstanding; /* Wait if there are more pending jobs than allowed in the * window. But spawn a new job anyway if no events are happening * after we wait for some time. */ for (i=0; outstanding > (int)app.client.job_window && i<1000; ++i) { pj_time_val wait = { 0, 500 }; unsigned count = 0; pjsip_endpt_handle_events2(app.sip_endpt, &wait, &count); outstanding = app.client.job_submitted - app.client.job_finished; if (count == 0) break; ++cycle; } /* Submit one job */ if (app.client.method.id == PJSIP_INVITE_METHOD) { status = make_call(&app.client.dst_uri); } else if (app.client.stateless) { status = submit_stateless_job(); } else { status = submit_job(); } ++app.client.job_submitted; ++cycle; /* Handle event */ pjsip_endpt_handle_events2(app.sip_endpt, &timeout, NULL); /* Check for time out, also print report */ if (cycle - last_cycle >= 500) { pj_gettimeofday(&now); if (PJ_TIME_VAL_GTE(now, end_time)) { break; } last_cycle = cycle; if (thread_index == 0 && now.sec-last_report.sec >= 2) { printf("\r%d jobs started, %d completed... ", app.client.job_submitted, app.client.job_finished); fflush(stdout); last_report = now; } } } if (app.client.requests_sent.sec == 0) { pj_gettimeofday(&app.client.requests_sent); } if (thread_index == 0) { printf("\r%d jobs started, %d completed%s\n", app.client.job_submitted, app.client.job_finished, (app.client.job_submitted!=app.client.job_finished ? ", waiting..." : ".") ); fflush(stdout); } /* Wait until all jobs completes, or timed out */ pj_gettimeofday(&now); while (PJ_TIME_VAL_LT(now, end_time) && app.client.job_finished < app.client.job_count && !app.thread_quit) { pj_time_val timeout = { 0, 1 }; unsigned i; for (i=0; i<1000; ++i) { unsigned count; count = 0; pjsip_endpt_handle_events2(app.sip_endpt, &timeout, &count); if (count == 0) break; } pj_gettimeofday(&now); } /* Wait couple of seconds to let jobs completes (e.g. ACKs to be sent) */ pj_gettimeofday(&now); end_time = now; end_time.sec += 2; while (PJ_TIME_VAL_LT(now, end_time)) { pj_time_val timeout = { 0, 1 }; unsigned i; for (i=0; i<1000; ++i) { unsigned count; count = 0; pjsip_endpt_handle_events2(app.sip_endpt, &timeout, &count); if (count == 0) break; } pj_gettimeofday(&now); } return 0; }
bool operator >= (const Pj_Time_Val &rhs) const { return PJ_TIME_VAL_GTE((*this), rhs); }
/* Test that we receive loopback message. */ int transport_send_recv_test( pjsip_transport_type_e tp_type, pjsip_transport *ref_tp, char *target_url, int *p_usec_rtt) { pj_bool_t msg_log_enabled; pj_status_t status; pj_str_t target, from, to, contact, call_id, body; pjsip_method method; pjsip_tx_data *tdata; pj_time_val timeout; PJ_UNUSED_ARG(tp_type); PJ_UNUSED_ARG(ref_tp); PJ_LOG(3,(THIS_FILE, " single message round-trip test...")); /* Register out test module to receive the message (if necessary). */ if (my_module.id == -1) { status = pjsip_endpt_register_module( endpt, &my_module ); if (status != PJ_SUCCESS) { app_perror(" error: unable to register module", status); return -500; } } /* Disable message logging. */ msg_log_enabled = msg_logger_set_enabled(0); /* Create a request message. */ target = pj_str(target_url); from = pj_str(FROM_HDR); to = pj_str(target_url); contact = pj_str(CONTACT_HDR); call_id = pj_str(CALL_ID_HDR); body = pj_str(BODY); pjsip_method_set(&method, PJSIP_OPTIONS_METHOD); status = pjsip_endpt_create_request( endpt, &method, &target, &from, &to, &contact, &call_id, CSEQ_VALUE, &body, &tdata ); if (status != PJ_SUCCESS) { app_perror(" error: unable to create request", status); return -510; } /* Reset statuses */ send_status = recv_status = NO_STATUS; /* Start time. */ pj_get_timestamp(&my_send_time); /* Send the message (statelessly). */ PJ_LOG(5,(THIS_FILE, "Sending request to %.*s", (int)target.slen, target.ptr)); status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL, &send_msg_callback); if (status != PJ_SUCCESS) { /* Immediate error! */ pjsip_tx_data_dec_ref(tdata); send_status = status; } /* Set the timeout (2 seconds from now) */ pj_gettimeofday(&timeout); timeout.sec += 2; /* Loop handling events until we get status */ do { pj_time_val now; pj_time_val poll_interval = { 0, 10 }; pj_gettimeofday(&now); if (PJ_TIME_VAL_GTE(now, timeout)) { PJ_LOG(3,(THIS_FILE, " error: timeout in send/recv test")); status = -540; goto on_return; } if (send_status!=NO_STATUS && send_status!=PJ_SUCCESS) { app_perror(" error sending message", send_status); status = -550; goto on_return; } if (recv_status!=NO_STATUS && recv_status!=PJ_SUCCESS) { app_perror(" error receiving message", recv_status); status = -560; goto on_return; } if (send_status!=NO_STATUS && recv_status!=NO_STATUS) { /* Success! */ break; } pjsip_endpt_handle_events(endpt, &poll_interval); } while (1); if (status == PJ_SUCCESS) { unsigned usec_rt; usec_rt = pj_elapsed_usec(&my_send_time, &my_recv_time); PJ_LOG(3,(THIS_FILE, " round-trip = %d usec", usec_rt)); *p_usec_rtt = usec_rt; } /* Restore message logging. */ msg_logger_set_enabled(msg_log_enabled); status = PJ_SUCCESS; on_return: return status; }
/* This is the transmission "tick". * This function is called periodically every "tick" milliseconds, and * it will determine whether to transmit packet(s) (or to drop it). */ static void tx_tick(const pj_time_val *t) { struct stream *strm = g_app.tx; static char log_msg[120]; pjmedia_port *port = g_app.tx->port; long pkt_interval; /* packet interval, without jitter */ pkt_interval = PJMEDIA_PIA_SPF(&port->info) * 1000 / PJMEDIA_PIA_SRATE(&port->info); while (PJ_TIME_VAL_GTE(*t, strm->state.tx.next_schedule)) { struct log_entry entry; pj_bool_t drop_this_pkt = PJ_FALSE; int jitter; /* Init log entry */ pj_bzero(&entry, sizeof(entry)); entry.wall_clock = *t; /* * Determine whether to drop this packet */ if (strm->state.tx.cur_lost_burst) { /* We are currently dropping packet */ /* Make it comply to minimum lost burst */ if (strm->state.tx.cur_lost_burst < g_app.cfg.tx_min_lost_burst) { drop_this_pkt = PJ_TRUE; } /* Correlate the next packet loss */ if (!drop_this_pkt && strm->state.tx.cur_lost_burst < g_app.cfg.tx_max_lost_burst && MAX(strm->state.tx.total_lost-LOSS_EXTRA,0) * 100 / MAX(strm->state.tx.total_tx,1) < g_app.cfg.tx_pct_avg_lost ) { strm->state.tx.drop_prob = ((g_app.cfg.tx_pct_loss_corr * strm->state.tx.drop_prob) + ((100-g_app.cfg.tx_pct_loss_corr) * (pj_rand()%100)) ) / 100; if (strm->state.tx.drop_prob >= 100) strm->state.tx.drop_prob = 99; if (strm->state.tx.drop_prob >= 100 - g_app.cfg.tx_pct_avg_lost) drop_this_pkt = PJ_TRUE; } } /* If we're not dropping packet then use randomly distributed loss */ if (!drop_this_pkt && MAX(strm->state.tx.total_lost-LOSS_EXTRA,0) * 100 / MAX(strm->state.tx.total_tx,1) < g_app.cfg.tx_pct_avg_lost) { strm->state.tx.drop_prob = pj_rand() % 100; if (strm->state.tx.drop_prob >= 100 - g_app.cfg.tx_pct_avg_lost) drop_this_pkt = PJ_TRUE; } if (drop_this_pkt) { /* Drop the frame */ pjmedia_transport_simulate_lost(g_app.loop, PJMEDIA_DIR_ENCODING, 100); run_one_frame(g_app.tx_wav, g_app.tx->port, NULL); pjmedia_transport_simulate_lost(g_app.loop, PJMEDIA_DIR_ENCODING, 0); entry.event = EVENT_TX_DROP; entry.log = "** This packet was lost **"; ++strm->state.tx.total_lost; ++strm->state.tx.cur_lost_burst; } else { pjmedia_rtcp_stat stat; pjmedia_jb_state jstate; unsigned last_discard; pjmedia_stream_get_stat_jbuf(g_app.rx->strm, &jstate); last_discard = jstate.discard; run_one_frame(g_app.tx_wav, g_app.tx->port, NULL); pjmedia_stream_get_stat(g_app.rx->strm, &stat); pjmedia_stream_get_stat_jbuf(g_app.rx->strm, &jstate); entry.event = EVENT_TX; entry.jb_state = &jstate; entry.stat = &stat; entry.log = log_msg; if (jstate.discard > last_discard) strcat(log_msg, "** Note: packet was discarded by jitter buffer **"); strm->state.tx.cur_lost_burst = 0; } write_log(&entry, PJ_TRUE); ++strm->state.tx.total_tx; /* Calculate next schedule */ strm->state.tx.next_schedule.sec = 0; strm->state.tx.next_schedule.msec = (strm->state.tx.total_tx + 1) * pkt_interval; /* Apply jitter */ if (g_app.cfg.tx_max_jitter || g_app.cfg.tx_min_jitter) { if (g_app.cfg.tx_max_jitter == g_app.cfg.tx_min_jitter) { /* Fixed jitter */ switch (pj_rand() % 3) { case 0: jitter = 0 - g_app.cfg.tx_min_jitter; break; case 2: jitter = g_app.cfg.tx_min_jitter; break; default: jitter = 0; break; } } else { int jitter_range; jitter_range = (g_app.cfg.tx_max_jitter-g_app.cfg.tx_min_jitter)*2; jitter = pj_rand() % jitter_range; if (jitter < jitter_range/2) { jitter = 0 - g_app.cfg.tx_min_jitter - (jitter/2); } else { jitter = g_app.cfg.tx_min_jitter + (jitter/2); } } } else { jitter = 0; } pj_time_val_normalize(&strm->state.tx.next_schedule); sprintf(log_msg, "** Packet #%u tick is at %d.%03d, %d ms jitter applied **", strm->state.tx.total_tx+1, (int)strm->state.tx.next_schedule.sec, (int)strm->state.tx.next_schedule.msec, jitter); strm->state.tx.next_schedule.msec += jitter; pj_time_val_normalize(&strm->state.tx.next_schedule); } /* while */ }