PJ_DEF(pj_status_t) pj_activesock_send( pj_activesock_t *asock, pj_ioqueue_op_key_t *send_key, const void *data, pj_ssize_t *size, unsigned flags) { PJ_ASSERT_RETURN(asock && send_key && data && size, PJ_EINVAL); if (asock->shutdown & SHUT_TX) return PJ_EINVALIDOP; send_key->activesock_data = NULL; if (asock->whole_data) { pj_ssize_t whole; pj_status_t status; whole = *size; status = pj_ioqueue_send(asock->key, send_key, data, size, flags); if (status != PJ_SUCCESS) { /* Pending or error */ return status; } if (*size == whole) { /* The whole data has been sent. */ return PJ_SUCCESS; } /* Data was partially sent */ asock->send_data.data = (pj_uint8_t*)data; asock->send_data.len = whole; asock->send_data.sent = *size; asock->send_data.flags = flags; send_key->activesock_data = &asock->send_data; /* Try again */ status = send_remaining(asock, send_key); if (status == PJ_SUCCESS) { *size = whole; } return status; } else { return pj_ioqueue_send(asock->key, send_key, data, size, flags); } }
/* Callback when data has been written. * Increment item->bytes_sent and write the next data. */ static void on_write_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_sent) { test_item *item = (test_item*) pj_ioqueue_get_user_data(key); //TRACE_((THIS_FILE, " write complete: sent = %d", bytes_sent)); if (thread_quit_flag) return; item->has_pending_send = 0; item->bytes_sent += bytes_sent; if (bytes_sent <= 0) { PJ_LOG(3,(THIS_FILE, "...error: sending stopped. bytes_sent=%d", bytes_sent)); } else { pj_status_t rc; bytes_sent = item->buffer_size; rc = pj_ioqueue_send( item->client_key, op_key, item->outgoing_buffer, &bytes_sent, 0); if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { app_perror("...error: write error", rc); } item->has_pending_send = (rc==PJ_EPENDING); } }
static pj_status_t send_remaining(pj_activesock_t *asock, pj_ioqueue_op_key_t *send_key) { struct send_data *sd = (struct send_data*)send_key->activesock_data; pj_status_t status; do { pj_ssize_t size; size = sd->len - sd->sent; status = pj_ioqueue_send(asock->key, send_key, sd->data+sd->sent, &size, sd->flags); if (status != PJ_SUCCESS) { /* Pending or error */ break; } sd->sent += size; if (sd->sent == sd->len) { /* The whole data has been sent. */ return PJ_SUCCESS; } } while (sd->sent < sd->len); return status; }
/* Callback when data has been read. * Increment item->bytes_recv and ready to read the next data. */ static void on_read_complete(pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, pj_ssize_t bytes_read) { test_item *item = (test_item*)pj_ioqueue_get_user_data(key); pj_status_t rc; int data_is_available = 1; //TRACE_((THIS_FILE, " read complete, bytes_read=%d", bytes_read)); do { if (thread_quit_flag) return; if (bytes_read < 0) { char errmsg[PJ_ERR_MSG_SIZE]; rc = (pj_status_t)-bytes_read; if (rc != last_error) { //last_error = rc; pj_strerror(rc, errmsg, sizeof(errmsg)); PJ_LOG(3,(THIS_FILE,"...error: read error, bytes_read=%d (%s)", bytes_read, errmsg)); PJ_LOG(3,(THIS_FILE, ".....additional info: total read=%u, total sent=%u", item->bytes_recv, item->bytes_sent)); } else { last_error_counter++; } bytes_read = 0; } else if (bytes_read == 0) { PJ_LOG(3,(THIS_FILE, "...socket has closed!")); } item->bytes_recv += bytes_read; /* To assure that the test quits, even if main thread * doesn't have time to run. */ if (item->bytes_recv > item->buffer_size * 10000) thread_quit_flag = 1; bytes_read = item->buffer_size; rc = pj_ioqueue_recv( key, op_key, item->incoming_buffer, &bytes_read, 0 ); if (rc == PJ_SUCCESS) { data_is_available = 1; } else if (rc == PJ_EPENDING) { data_is_available = 0; } else { data_is_available = 0; if (rc != last_error) { last_error = rc; app_perror("...error: read error(1)", rc); } else { last_error_counter++; } } if (!item->has_pending_send) { pj_ssize_t sent = item->buffer_size; rc = pj_ioqueue_send(item->client_key, &item->send_op, item->outgoing_buffer, &sent, 0); if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { app_perror("...error: write error", rc); } item->has_pending_send = (rc==PJ_EPENDING); } } while (data_is_available); }
/* Calculate the bandwidth for the specific test configuration. * The test is simple: * - create sockpair_cnt number of producer-consumer socket pair. * - create thread_cnt number of worker threads. * - each producer will send buffer_size bytes data as fast and * as soon as it can. * - each consumer will read buffer_size bytes of data as fast * as it could. * - measure the total bytes received by all consumers during a * period of time. */ static int perform_test(pj_bool_t allow_concur, int sock_type, const char *type_name, unsigned thread_cnt, unsigned sockpair_cnt, pj_size_t buffer_size, pj_size_t *p_bandwidth) { enum { MSEC_DURATION = 5000 }; pj_pool_t *pool; test_item *items; pj_thread_t **thread; pj_ioqueue_t *ioqueue; pj_status_t rc; pj_ioqueue_callback ioqueue_callback; pj_uint32_t total_elapsed_usec, total_received; pj_highprec_t bandwidth; pj_timestamp start, stop; unsigned i; TRACE_((THIS_FILE, " starting test..")); ioqueue_callback.on_read_complete = &on_read_complete; ioqueue_callback.on_write_complete = &on_write_complete; thread_quit_flag = 0; pool = pj_pool_create(mem, NULL, 4096, 4096, NULL); if (!pool) return -10; items = (test_item*) pj_pool_alloc(pool, sockpair_cnt*sizeof(test_item)); thread = (pj_thread_t**) pj_pool_alloc(pool, thread_cnt*sizeof(pj_thread_t*)); TRACE_((THIS_FILE, " creating ioqueue..")); rc = pj_ioqueue_create(pool, sockpair_cnt*2, &ioqueue); if (rc != PJ_SUCCESS) { app_perror("...error: unable to create ioqueue", rc); return -15; } rc = pj_ioqueue_set_default_concurrency(ioqueue, allow_concur); if (rc != PJ_SUCCESS) { app_perror("...error: pj_ioqueue_set_default_concurrency()", rc); return -16; } /* Initialize each producer-consumer pair. */ for (i=0; i<sockpair_cnt; ++i) { pj_ssize_t bytes; items[i].ioqueue = ioqueue; items[i].buffer_size = buffer_size; items[i].outgoing_buffer = (char*) pj_pool_alloc(pool, buffer_size); items[i].incoming_buffer = (char*) pj_pool_alloc(pool, buffer_size); items[i].bytes_recv = items[i].bytes_sent = 0; /* randomize outgoing buffer. */ pj_create_random_string(items[i].outgoing_buffer, buffer_size); /* Create socket pair. */ TRACE_((THIS_FILE, " calling socketpair..")); rc = app_socketpair(pj_AF_INET(), sock_type, 0, &items[i].server_fd, &items[i].client_fd); if (rc != PJ_SUCCESS) { app_perror("...error: unable to create socket pair", rc); return -20; } /* Register server socket to ioqueue. */ TRACE_((THIS_FILE, " register(1)..")); rc = pj_ioqueue_register_sock(pool, ioqueue, items[i].server_fd, &items[i], &ioqueue_callback, &items[i].server_key); if (rc != PJ_SUCCESS) { app_perror("...error: registering server socket to ioqueue", rc); return -60; } /* Register client socket to ioqueue. */ TRACE_((THIS_FILE, " register(2)..")); rc = pj_ioqueue_register_sock(pool, ioqueue, items[i].client_fd, &items[i], &ioqueue_callback, &items[i].client_key); if (rc != PJ_SUCCESS) { app_perror("...error: registering server socket to ioqueue", rc); return -70; } /* Start reading. */ TRACE_((THIS_FILE, " pj_ioqueue_recv..")); bytes = items[i].buffer_size; rc = pj_ioqueue_recv(items[i].server_key, &items[i].recv_op, items[i].incoming_buffer, &bytes, 0); if (rc != PJ_EPENDING) { app_perror("...error: pj_ioqueue_recv", rc); return -73; } /* Start writing. */ TRACE_((THIS_FILE, " pj_ioqueue_write..")); bytes = items[i].buffer_size; rc = pj_ioqueue_send(items[i].client_key, &items[i].send_op, items[i].outgoing_buffer, &bytes, 0); if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { app_perror("...error: pj_ioqueue_write", rc); return -76; } items[i].has_pending_send = (rc==PJ_EPENDING); } /* Create the threads. */ for (i=0; i<thread_cnt; ++i) { struct thread_arg *arg; arg = (struct thread_arg*) pj_pool_zalloc(pool, sizeof(*arg)); arg->id = i; arg->ioqueue = ioqueue; arg->counter = 0; rc = pj_thread_create( pool, NULL, &worker_thread, arg, PJ_THREAD_DEFAULT_STACK_SIZE, PJ_THREAD_SUSPENDED, &thread[i] ); if (rc != PJ_SUCCESS) { app_perror("...error: unable to create thread", rc); return -80; } } /* Mark start time. */ rc = pj_get_timestamp(&start); if (rc != PJ_SUCCESS) return -90; /* Start the thread. */ TRACE_((THIS_FILE, " resuming all threads..")); for (i=0; i<thread_cnt; ++i) { rc = pj_thread_resume(thread[i]); if (rc != 0) return -100; } /* Wait for MSEC_DURATION seconds. * This should be as simple as pj_thread_sleep(MSEC_DURATION) actually, * but unfortunately it doesn't work when system doesn't employ * timeslicing for threads. */ TRACE_((THIS_FILE, " wait for few seconds..")); do { pj_thread_sleep(1); /* Mark end time. */ rc = pj_get_timestamp(&stop); if (thread_quit_flag) { TRACE_((THIS_FILE, " transfer limit reached..")); break; } if (pj_elapsed_usec(&start,&stop)<MSEC_DURATION * 1000) { TRACE_((THIS_FILE, " time limit reached..")); break; } } while (1); /* Terminate all threads. */ TRACE_((THIS_FILE, " terminating all threads..")); thread_quit_flag = 1; for (i=0; i<thread_cnt; ++i) { TRACE_((THIS_FILE, " join thread %d..", i)); pj_thread_join(thread[i]); } /* Close all sockets. */ TRACE_((THIS_FILE, " closing all sockets..")); for (i=0; i<sockpair_cnt; ++i) { pj_ioqueue_unregister(items[i].server_key); pj_ioqueue_unregister(items[i].client_key); } /* Destroy threads */ for (i=0; i<thread_cnt; ++i) { pj_thread_destroy(thread[i]); } /* Destroy ioqueue. */ TRACE_((THIS_FILE, " destroying ioqueue..")); pj_ioqueue_destroy(ioqueue); /* Calculate actual time in usec. */ total_elapsed_usec = pj_elapsed_usec(&start, &stop); /* Calculate total bytes received. */ total_received = 0; for (i=0; i<sockpair_cnt; ++i) { total_received = (pj_uint32_t)items[i].bytes_recv; } /* bandwidth = total_received*1000/total_elapsed_usec */ bandwidth = total_received; pj_highprec_mul(bandwidth, 1000); pj_highprec_div(bandwidth, total_elapsed_usec); *p_bandwidth = (pj_uint32_t)bandwidth; PJ_LOG(3,(THIS_FILE, " %.4s %2d %2d %8d KB/s", type_name, thread_cnt, sockpair_cnt, *p_bandwidth)); /* Done. */ pj_pool_release(pool); TRACE_((THIS_FILE, " done..")); return 0; }
static int send_recv_test(pj_ioqueue_t *ioque, pj_ioqueue_key_t *skey, pj_ioqueue_key_t *ckey, void *send_buf, void *recv_buf, pj_ssize_t bufsize, pj_timestamp *t_elapsed) { pj_status_t status; pj_ssize_t bytes; pj_time_val timeout; pj_timestamp t1, t2; int pending_op = 0; pj_ioqueue_op_key_t read_op, write_op; // Start reading on the server side. bytes = bufsize; status = pj_ioqueue_recv(skey, &read_op, recv_buf, &bytes, 0); if (status != PJ_SUCCESS && status != PJ_EPENDING) { app_perror("...pj_ioqueue_recv error", status); return -100; } if (status == PJ_EPENDING) ++pending_op; else { /* Does not expect to return error or immediate data. */ return -115; } // Randomize send buffer. pj_create_random_string((char*)send_buf, bufsize); // Starts send on the client side. bytes = bufsize; status = pj_ioqueue_send(ckey, &write_op, send_buf, &bytes, 0); if (status != PJ_SUCCESS && bytes != PJ_EPENDING) { return -120; } if (status == PJ_EPENDING) { ++pending_op; } // Begin time. pj_get_timestamp(&t1); // Reset indicators callback_read_size = callback_write_size = 0; callback_read_key = callback_write_key = NULL; callback_read_op = callback_write_op = NULL; // Poll the queue until we've got completion event in the server side. status = 0; while (pending_op > 0) { timeout.sec = 1; timeout.msec = 0; #ifdef PJ_SYMBIAN PJ_UNUSED_ARG(ioque); status = pj_symbianos_poll(-1, 1000); #else status = pj_ioqueue_poll(ioque, &timeout); #endif if (status > 0) { if (callback_read_size) { if (callback_read_size != bufsize) return -160; if (callback_read_key != skey) return -161; if (callback_read_op != &read_op) return -162; } if (callback_write_size) { if (callback_write_key != ckey) return -163; if (callback_write_op != &write_op) return -164; } pending_op -= status; } if (status == 0) { PJ_LOG(3,("", "...error: timed out")); } if (status < 0) { return -170; } } // Pending op is zero. // Subsequent poll should yield zero too. timeout.sec = timeout.msec = 0; #ifdef PJ_SYMBIAN status = pj_symbianos_poll(-1, 1); #else status = pj_ioqueue_poll(ioque, &timeout); #endif if (status != 0) return -173; // End time. pj_get_timestamp(&t2); t_elapsed->u32.lo += (t2.u32.lo - t1.u32.lo); // Compare recv buffer with send buffer. if (pj_memcmp(send_buf, recv_buf, bufsize) != 0) { return -180; } // Success return 0; }
// // Start async send() // pj_status_t send( Pj_Async_Op *op_key, const void *data, pj_ssize_t *len, unsigned flags) { return pj_ioqueue_send( key_, op_key, data, len, flags); }