20 #include <sys/socket.h> 21 #include <sys/select.h> 22 #include <netinet/in.h> 25 #include "LLNET_Common.h" 38 #if ASYNC_SELECT_CONFIGURATION_VERSION != 2 40 #error "Version of the configuration file async_select_configuration.h is not compatible with this implementation." 47 int32_t java_thread_id;
49 int64_t absolute_timeout_ms;
72 #define async_select_get_current_time_ms() LLMJVM_IMPL_getCurrentTime__Z(1) // 1 means that system time is required 78 static void async_select_do_select(
void);
79 static void async_select_update_notified_requests(
void);
80 static int32_t async_select_get_notify_fd(
void);
85 static void async_select_notify_select(
void);
103 static fd_set read_fds;
107 static fd_set write_fds;
112 #ifdef ASYNC_SELECT_USE_PIPE_FOR_NOTIFICATION 114 static int8_t pipe_fds_initialized = 0;
115 static int32_t pipe_fds[2];
119 volatile static int32_t notify_fd_cache = -1;
121 #endif //ASYNC_SELECT_USE_PIPE_FOR_NOTIFICATION 126 volatile static uint8_t async_select_fifo_initialized = 0;
137 struct timeval zero_timeout;
138 zero_timeout.tv_sec = 0;
139 zero_timeout.tv_usec = 0;
141 fd_set* read_fds_ptr;
142 fd_set* write_fds_ptr;
146 if(operation == SELECT_READ){
147 read_fds_ptr = &io_fds;
148 write_fds_ptr = NULL;
152 write_fds_ptr = &io_fds;
155 int32_t selectRes = select(fd+1, read_fds_ptr, write_fds_ptr, NULL, &zero_timeout);
184 int32_t java_thread_id = SNI_getCurrentJavaThreadID();
185 if(java_thread_id == SNI_ERROR){
187 async_select_free_unused_request(request);
191 LLNET_DEBUG_TRACE(
"async_select: async_select on fd=0x%X operation=%s thread 0x%X\n", fd, operation==SELECT_READ ?
"read":
"write", java_thread_id);
192 request->java_thread_id = java_thread_id;
194 request->operation = operation;
199 request->absolute_timeout_ms = 0;
202 SNI_suspendCurrentJavaThreadWithCallback(0, callback, NULL);
203 res = async_select_send_new_request(request);
216 if(async_select_fifo_initialized == 0){
217 free_requests_fifo = &all_requests[0];
219 all_requests[i].next = &all_requests[i+1];
221 all_requests[MAX_NB_ASYNC_SELECT-1].next = NULL;
224 used_requests_fifo = NULL;
225 async_select_fifo_initialized = 1;
239 #ifndef ASYNC_SELECT_CLOSE_UNBLOCK_SELECT 246 while(request != NULL){
247 if(request->fd == fd){
250 request->absolute_timeout_ms = 1;
252 request = request->next;
257 async_select_notify_select();
259 #endif //ASYNC_SELECT_CLOSE_UNBLOCK_SELECT 275 async_select_do_select();
277 async_select_update_notified_requests();
286 static int32_t async_select_get_notify_fd(){
289 #ifdef ASYNC_SELECT_USE_PIPE_FOR_NOTIFICATION 291 if(pipe_fds_initialized == 0){
292 if(pipe(pipe_fds) == -1 ||
293 set_socket_non_blocking(pipe_fds[0],
true) != 0 ||
294 set_socket_non_blocking(pipe_fds[1],
true) != 0){
298 pipe_fds_initialized = 1;
308 int32_t notify_fd = notify_fd_cache;
318 #if LLNET_AF & LLNET_AF_IPV6 324 notify_fd = llnet_socket(domain, SOCK_STREAM, IPPROTO_TCP);
328 #if LLNET_AF & LLNET_AF_IPV6 329 struct sockaddr_in6 sockaddr = {0};
330 sockaddr.sin6_family = AF_INET6;
331 sockaddr.sin6_port = llnet_htons(0);
332 sockaddr.sin6_addr = in6addr_loopback;
334 struct sockaddr_in sockaddr = {0};
335 sockaddr.sin_family = AF_INET;
336 sockaddr.sin_port = htons(0);
337 sockaddr.sin_addr.s_addr = INADDR_LOOPBACK;
339 int32_t ret = llnet_bind(notify_fd, (
struct sockaddr*)&sockaddr,
sizeof(sockaddr));
341 ret = llnet_listen(notify_fd, 1);
344 notify_fd_cache = notify_fd;
350 llnet_close(notify_fd);
355 #endif // ASYNC_SELECT_USE_PIPE_FOR_NOTIFICATION 361 static void async_select_do_select(){
365 int32_t notify_fd = async_select_get_notify_fd();
367 int32_t max_request_fd = notify_fd;
369 int64_t min_absolute_timeout_ms = INT64_MAX;
376 FD_SET(notify_fd, &read_fds);
383 LLNET_DEBUG_TRACE(
"async_select: WARNING: notify_fd cannot be allocated, fall back in polling mode\n");
389 request = used_requests_fifo;
390 while(request != NULL){
391 int32_t request_fd = request->fd;
392 if(request_fd > max_request_fd){
394 max_request_fd = request_fd;
397 int64_t request_absolute_timeout_ms = request->absolute_timeout_ms;
398 if(request_absolute_timeout_ms != 0 && request_absolute_timeout_ms < min_absolute_timeout_ms){
400 min_absolute_timeout_ms = request_absolute_timeout_ms;
403 if(request->operation == SELECT_READ){
404 FD_SET(request_fd, &read_fds);
407 FD_SET(request_fd, &write_fds);
410 request = request->next;
418 struct timeval select_timeout = {0};
419 struct timeval* select_timeout_ptr;
421 if(min_absolute_timeout_ms != INT64_MAX){
423 select_timeout_ptr = &select_timeout;
426 if(min_relative_timeout_ms < 0){
428 min_relative_timeout_ms = 0;
430 time_ms_to_timeval(min_relative_timeout_ms, select_timeout_ptr);
434 select_timeout_ptr = NULL;
441 LLNET_DEBUG_TRACE(
"async_select: select (timeout sec=%d usec=%d)\n", (int32_t)select_timeout.tv_sec, (int32_t)select_timeout.tv_usec);
442 int32_t res = select(max_request_fd+1, &read_fds, &write_fds, NULL, select_timeout_ptr);
444 if(res >= 0 || llnet_errno(-1) == EBADF){
449 #ifdef ASYNC_SELECT_USE_PIPE_FOR_NOTIFICATION 451 if(FD_ISSET(notify_fd, &read_fds)){
454 while(read(notify_fd, (
void*)bytes, 1) > 0);
458 LLNET_DEBUG_TRACE(
"async_select: select finished %d sockets available\n", res);
466 static void async_select_update_notified_requests(){
474 request = used_requests_fifo;
475 while(request != NULL){
476 int32_t request_fd = request->fd;
477 bool request_timeout_reached;
480 if(request->absolute_timeout_ms != 0 && request->absolute_timeout_ms <= current_time_ms){
481 request_timeout_reached =
true;
484 request_timeout_reached =
false;
487 if((request->operation == SELECT_READ && FD_ISSET(request_fd, &read_fds))
488 || (request->operation == SELECT_WRITE && FD_ISSET(request_fd, &write_fds))
489 || (request_timeout_reached)
492 LLNET_DEBUG_TRACE(
"async_select: request done for fd=0x%X operation=%s notify thread 0x%X (%s)\n", request_fd, request->operation==SELECT_READ ?
"read":
"write", request->java_thread_id, request_timeout_reached==
true ?
"timeout":
"no timeout");
493 SNI_resumeJavaThread(request->java_thread_id);
494 request = async_select_free_used_request(request, previous_request);
498 previous_request = request;
499 request = request->next;
516 next_request = request->next;
519 if(previous_request_in_used_fifo != NULL){
520 previous_request_in_used_fifo->next = next_request;
524 used_requests_fifo = next_request;
528 request->next = free_requests_fifo;
529 free_requests_fifo = request;
545 request->next = free_requests_fifo;
546 free_requests_fifo = request;
558 request->next = used_requests_fifo;
559 used_requests_fifo = request;
563 async_select_notify_select();
583 if(new_request != NULL){
585 free_requests_fifo = new_request->next;
599 static void async_select_notify_select(){
604 #ifdef ASYNC_SELECT_USE_PIPE_FOR_NOTIFICATION 606 if(pipe_fds_initialized == 1){
609 notify_fd = pipe_fds[1];
610 res = write(notify_fd, (
void*)bytes, 1);
620 notify_fd = notify_fd_cache;
627 notify_fd_cache = -1;
628 res = llnet_close(notify_fd);
638 #endif // ASYNC_SELECT_USE_PIPE_FOR_NOTIFICATION 641 LLNET_DEBUG_TRACE(
"Error on notify select (notify_fd: 0x%X errno: %d)\n", notify_fd, llnet_errno(notify_fd));
int64_t LLMJVM_IMPL_getCurrentTime__Z(uint8_t system)
External function used to retrieve currentTime (defined in LLMJVM)
void async_select_unlock(void)
Exit critical section for the async_select component.
int32_t non_blocking_select(int32_t fd, SELECT_Operation operation)
Execute a select() for the given file descriptor and operation without blocking.
void async_select_lock(void)
Enter critical section for the async_select component.
#define async_select_get_current_time_ms()
void async_select_request_fifo_init(void)
Initializes the requests FIFOs. This function must be called prior to any call of async_select()...
An asynchronous select request.
void async_select_task_main()
The entry point for the async_select task. This function must be called from a dedicated task...
Asynchronous network select configuration.
#define ASYNC_SELECT_POLLING_MODE_TIMEOUT_MS
Timeout in milliseconds used when the async_select task cannot allocate a socket for notifications...
struct async_select_Request async_select_Request
An asynchronous select request.
void async_select_notify_closed_fd(int32_t fd)
Notifies the async_select task that a file descriptor has been closed. On some systems the close of a...
#define MAX_NB_ASYNC_SELECT
Maximum number of asynchronous select that can be done at the same moment.
SELECT_Operation
Select operations list.
int32_t async_select(int32_t fd, SELECT_Operation operation, int64_t timeout_ms, SNI_callback callback)
Executes asynchronously a select() operation for the given file descriptor.
Asynchronous network select API.