/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ /* * * This file implements the socket polling functions that * allow us to quickly determine if there is anything for * netlib to do at any instance in time. These functions * previously resided in the FE's but now have been consolidated * in the backend as part of the NSPR20 transition, since we * are no longer using winsock async socket notification. * * Designed and implemented by Lou Montulli '98 */ #include "mkutils.h" #include "mkselect.h" #include "nslocks.h" typedef enum { ConnectSelect, ReadSelect } SelectType; /* define this to use FE timers for the call_netlib_all_the_time * function. Otherwise we will use NET_PollSockets to perform the * same functionality */ #ifdef XP_WIN #undef USE_TIMERS_FOR_CALL_ALL_THE_TIME #else #define USE_TIMERS_FOR_CALL_ALL_THE_TIME #endif #define MAX_SIMULTANIOUS_SOCKETS 100 /* should never have more than MAX sockets */ PRPollDesc poll_desc_array[MAX_SIMULTANIOUS_SOCKETS]; unsigned int fd_set_size = 0; PRIVATE int net_calling_all_the_time_count=0; PRIVATE XP_Bool net_slow_timer_on=FALSE; /* Add a select entry, no duplicates. */ PRIVATE void net_add_select(SelectType stType, PRFileDesc *prFD) { unsigned int index = fd_set_size; unsigned int count; #define CONNECT_FLAGS (PR_POLL_READ | PR_POLL_EXCEPT | PR_POLL_WRITE) #define READ_FLAGS (PR_POLL_READ | PR_POLL_EXCEPT) /* * Make sure this function is called inside of the LIBNET lock since * it modifies global data structures... */ PR_ASSERT(LIBNET_IS_LOCKED()); /* Go through the list, make sure that there is not already an entry for fd. */ for(count=0; count < fd_set_size; count++) { if(poll_desc_array[count].fd == prFD) { /* found it */ /* verify that it has the same flags that we wan't, * otherwise add it again with different flags */ if((stType == ConnectSelect && poll_desc_array[count].in_flags == CONNECT_FLAGS) || (stType == ReadSelect && poll_desc_array[count].in_flags == READ_FLAGS)) { index = count; break; } } } /* need to add a new one if we didnt' find the index */ if(index == fd_set_size) { poll_desc_array[fd_set_size].fd = prFD; fd_set_size++; } if(stType == ConnectSelect) poll_desc_array[index].in_flags = CONNECT_FLAGS; else if(stType == ReadSelect) poll_desc_array[index].in_flags = READ_FLAGS; else PR_ASSERT(0); } /* Remove a select if it exists. */ PRIVATE void net_remove_select(SelectType stType, PRFileDesc *prFD) { unsigned int count; /* * Make sure this function is called inside of the LIBNET lock since * it modifies global data structures... */ PR_ASSERT(LIBNET_IS_LOCKED()); /* Go through the list */ for(count=0; count < fd_set_size; count++) { if(poll_desc_array[count].fd == prFD) { if((stType == ConnectSelect && poll_desc_array[count].in_flags == CONNECT_FLAGS) || (stType == ReadSelect && poll_desc_array[count].in_flags == READ_FLAGS)) { /* found it collapse the list */ fd_set_size--; if(count < fd_set_size) memmove(&poll_desc_array[count], &poll_desc_array[count+1], (fd_set_size - count) * sizeof(PRPollDesc)); return; } } } /* didn't find it. opps */ } MODULE_PRIVATE void NET_SetReadPoll(PRFileDesc *fd) { net_add_select(ReadSelect, fd); } MODULE_PRIVATE void NET_ClearReadPoll(PRFileDesc *fd) { net_remove_select(ReadSelect, fd); } MODULE_PRIVATE void NET_SetConnectPoll(PRFileDesc *fd) { net_add_select(ConnectSelect, fd); } MODULE_PRIVATE void NET_ClearConnectPoll(PRFileDesc *fd) { net_remove_select(ConnectSelect, fd); } /* call PR_Poll and call Netlib if necessary * * return FALSE if nothing to do. */ PUBLIC XP_Bool NET_PollSockets(void) { static PRIntervalTime interval = 0; register unsigned int itmp; /* * Enter the LIBNET lock to protect the poll_desc_array and other global * data structures used by NET_PollSockets(...) */ LIBNET_LOCK(); if(net_calling_all_the_time_count) NET_ProcessNet(NULL, NET_EVERYTIME_TYPE); if(!interval) interval = PR_MillisecondsToInterval(1); if(1 > fd_set_size) { LIBNET_UNLOCK(); return FALSE; } itmp = PR_Poll(poll_desc_array, fd_set_size, interval); if(itmp < 1) { LIBNET_UNLOCK(); return TRUE; /* potential for doing stuff in the future. */ } /* for now call on all active sockets. */ /* if this is too much call only one, but reorder the list each time. */ for(itmp=0; itmp < fd_set_size; itmp++) { if(poll_desc_array[itmp].out_flags) NET_ProcessNet(poll_desc_array[itmp].fd, NET_SOCKET_FD); } LIBNET_UNLOCK(); return TRUE; } void net_process_net_timer_callback(void *closure) { PR_ASSERT(net_calling_all_the_time_count >= 0); if (net_calling_all_the_time_count == 0) return; if (NET_ProcessNet(NULL, NET_EVERYTIME_TYPE) == 0) net_calling_all_the_time_count = 0; else FE_SetTimeout(net_process_net_timer_callback, NULL, 1); } MODULE_PRIVATE void NET_SetCallNetlibAllTheTime(MWContext *context, char *caller) { PR_ASSERT(net_calling_all_the_time_count >= 0); #ifdef USE_TIMERS_FOR_CALL_ALL_THE_TIME if (net_calling_all_the_time_count == 0) FE_SetTimeout(net_process_net_timer_callback, NULL, 1); #endif /* USE_TIMERS_FOR_CALL_ALL_THE_TIME */ net_calling_all_the_time_count++; } #define SLOW_NETLIB_TIMER_INTERVAL_MILLISECONDS 10 void net_process_slow_net_timer_callback(void *closure) { if(!NET_ProcessNet(NULL, NET_EVERYTIME_TYPE)) net_slow_timer_on = FALSE; /* dont reset the timer */ else if (net_slow_timer_on) FE_SetTimeout(net_process_slow_net_timer_callback, NULL, SLOW_NETLIB_TIMER_INTERVAL_MILLISECONDS); } /* this function turns on and off a reasonably slow timer that will * push the netlib along even when it doesn't get any onIdle time. * this is unfortunately necessary on windows because when a modal * dialog is up it won't call the OnIdle loop which is currently the * source of our events. */ MODULE_PRIVATE void NET_SetNetlibSlowKickTimer(PRBool set) { if(net_slow_timer_on == set) return; /* do nothing */ net_slow_timer_on = set; /* call immediately */ if(net_slow_timer_on) FE_SetTimeout(net_process_slow_net_timer_callback, NULL, SLOW_NETLIB_TIMER_INTERVAL_MILLISECONDS); } MODULE_PRIVATE void NET_ClearCallNetlibAllTheTime(MWContext *context, char *caller) { PR_ASSERT(net_calling_all_the_time_count > 0); net_calling_all_the_time_count--; } MODULE_PRIVATE PRBool NET_IsCallNetlibAllTheTimeSet(MWContext *context, char *caller) { if(caller == NULL) { if(net_calling_all_the_time_count > 0) return PR_TRUE; } else { /* not implemented */ PR_ASSERT(0); } return PR_FALSE; } MODULE_PRIVATE void NET_ClearDNSSelect(MWContext *context, PRFileDesc *file_desc) { #if defined(XP_WIN) || (defined(XP_UNIX) && defined(UNIX_ASYNC_DNS)) || (defined(XP_OS2) && !defined(XP_OS2_DOUGSOCK)) /* FE_AbortDNSLookup(file_desc); */ #endif /* XP_WIN || (XP_UNIX && UNIX_ASYNC_DNS) || (defined(XP_OS2) && !defined(XP_OS2_DOUGSOCK) */ } MODULE_PRIVATE void NET_SetFileReadSelect(MWContext *context, int file_desc) { /* need to conver this over to NSPR PRFileDesc's before it will work */ /* FE_SetFileReadSelect(context, file_desc); */ } MODULE_PRIVATE void NET_ClearFileReadSelect(MWContext *context, int file_desc) { /* need to conver this over to NSPR PRFileDesc's before it will work */ /* FE_ClearReadSelect(context, file_desc); */ } MODULE_PRIVATE void NET_SetReadSelect(MWContext *context, PRFileDesc *file_desc) { NET_SetReadPoll(file_desc); } MODULE_PRIVATE void NET_ClearReadSelect(MWContext *context, PRFileDesc *file_desc) { NET_ClearReadPoll(file_desc); } MODULE_PRIVATE void NET_SetConnectSelect(MWContext *context, PRFileDesc *file_desc) { NET_SetConnectPoll(file_desc); } MODULE_PRIVATE void NET_ClearConnectSelect(MWContext *context, PRFileDesc *file_desc) { NET_ClearConnectPoll(file_desc); }