timer_xsb.c

00001 /* File:      timer_xsb.c
00002 ** Author(s): Songmei Yu, kifer
00003 ** Contact:   xsb-contact@cs.sunysb.edu
00004 **
00005 ** Copyright (C) The Research Foundation of SUNY, 1999
00006 ** 
00007 ** XSB is free software; you can redistribute it and/or modify it under the
00008 ** terms of the GNU Library General Public License as published by the Free
00009 ** Software Foundation; either version 2 of the License, or (at your option)
00010 ** any later version.
00011 ** 
00012 ** XSB is distributed in the hope that it will be useful, but WITHOUT ANY
00013 ** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00014 ** FOR A PARTICULAR PURPOSE.  See the GNU Library General Public License for
00015 ** more details.
00016 ** 
00017 ** You should have received a copy of the GNU Library General Public License
00018 ** along with XSB; if not, write to the Free Software Foundation,
00019 ** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00020 **
00021 ** $Id: timer_xsb.c,v 1.20 2006/02/03 18:14:13 tswift Exp $
00022 ** 
00023 */
00024 
00025 
00026 
00027 #undef __STRICT_ANSI__
00028 
00029 #include "xsb_debug.h"
00030 #include "xsb_config.h"
00031 
00032 #ifdef WIN_NT
00033 #include <windows.h>
00034 #include <winuser.h>
00035 #else /* UNIX */
00036 #include <sys/types.h>
00037 #include <stdlib.h>
00038 #include <signal.h>
00039 #include <unistd.h>
00040 #include <errno.h>
00041 #endif
00042 
00043 #include <stdio.h>
00044 
00045 #include "xsb_time.h"
00046 #include "cell_xsb.h"
00047 #include "error_xsb.h"
00048 #include "setjmp_xsb.h"
00049 #include "timer_xsb.h"
00050 #include "basicdefs.h"
00051 #include "thread_xsb.h"
00052 
00053 /* To set a timeout for a function call such as: 
00054   ... ...
00055   int foo(char *X, int Y, long Z);
00056   ... ...
00057  
00058    do the following steps:
00059 
00060                1: Define a structure
00061 
00062                   struct xsb_timeout {
00063                      xsbTimeoutInfo timeout_info;
00064                      ???  return_value;
00065                      ???  arg1;
00066                      .....
00067                      ???  arg_n;
00068                   }
00069 
00070                   that will be used to hold the parameters of foo().
00071                   The member TIMEOUT_INFO is mandatory and must be the
00072                   first in the structure.
00073                   The other members of the structure are optional and they are
00074                   intended to represent the return value and the arguments of
00075                   foo().
00076                   Next, make a wrapper, say
00077 
00078                       void new_foo(xsbTimeout *pptr)
00079 
00080                   The type xsbTimeout is a typedef to struct xsb_timeout.
00081                   It is defined in timer_xsb.h.
00082                   In our case (given the declaration of foo() above), the
00083                   xsb_timeout structure will look as follows: 
00084            
00085                    struct xsb_timeout {
00086                        xsbTimeoutInfo timeout_info;
00087                        int  return_value;
00088                        char *X;
00089                        int   Y;
00090                        long  Z;
00091                    }
00092                 
00093                Then the wrapper will look as follows:
00094 
00095                void new_foo(xsbTimeout *pptr) {
00096                    pptr->return_value = foo(pptr->X, pptr->Y, pptr->Z);
00097 
00098                    NOTIFY_PARENT_THREAD(pptr);
00099                }
00100     
00101        step 2: Instead of calling foo() directly, now call the generic timeout
00102                control function MAKE_TIMED_CALL:  
00103    
00104                int make_timed_call(CTXTdeclc xsbTimeout*, void (*)(xsbTimeout*));
00105 
00106                For instance, 
00107    
00108                xsbTimeout *pptr = NEW_TIMEOUT_OBJECT; // defined in timer_xsb.h
00109                int timeout_flag;
00110 
00111                // timeout is set in Prolog using set_timer/1 call
00112                if (CHECK_TIMER_SET) {
00113                   timeout=make_timed_call(pptr, new_foo);
00114                   if (timeout_flag == TIMER_SETUP_ERR) {
00115                      // problem setting up timer (Windows only)
00116                   } else if (timeout_flag) {
00117                     // timeout happened
00118                     .....
00119                   }
00120                } else {
00121                  // timer is not set
00122                  new_foo(pptr); 
00123                }
00124 
00125        step 3: Free the xsbTimeout object when appropriate.
00126 */
00127 
00128 /* TLS comment: the implementation differs depending on whether XSB is
00129  * multi-threaded, single threaded UNIX or single-threaded Windows.
00130  * In the mt case, a timed pthread is created and detached, and a
00131  * timed condition variable is set.  The timed condition variable is
00132  * responsible for waking up the calling thread, which then checks to
00133  * see whether the call has timed out, or has succeeded.  In the
00134  * single-thread case of UNIX, an alarm and SIGALRM signal is used;
00135  * In the single-thread case of Windows, thread messaging is used.
00136  */
00137 
00138 #ifdef MULTI_THREAD
00139 
00140 void init_machine(CTXTdeclc int, int, int, int);
00141 
00142 #else /* not multithreaded */
00143 
00144 #ifdef WIN_NT
00145 static int exitFlag = STILL_WAITING;
00146 static long timedThread;
00147 HANDLE sockEvent = NULL;
00148 #else /* UNIX */ 
00149 sigjmp_buf xsb_timer_env;
00150 #endif
00151 
00152 #endif /* MULTI_THREAD */
00153 
00154 #ifdef MULTI_THREAD
00155 int op_timed_out(CTXTdeclc xsbTimeout *timeout)
00156 {
00157   struct timespec wakeup_time;                            // time.h
00158   int rc;
00159 
00160   wakeup_time.tv_sec = time(NULL) + (int)pflags[SYS_TIMER];
00161   pthread_mutex_lock(&timeout->timeout_info.mutex);
00162   rc = pthread_cond_timedwait(&timeout->timeout_info.condition, 
00163                               &timeout->timeout_info.mutex, &wakeup_time);
00164   pthread_mutex_unlock(&timeout->timeout_info.mutex);
00165   if (rc != 0) {
00166     switch(rc) {
00167     case EINVAL:
00168       xsb_bug("pthread_cond_timedwait returned EINVAL");
00169       break;
00170     case ETIMEDOUT:
00171       break;
00172     case ENOMEM:
00173       xsb_error("Not enough memory to wait\n");
00174       break;
00175     default:
00176       xsb_bug("pthread_cond_timedwait returned an unexpected value (%d)\n", rc);      
00177     }
00178   }
00179   TURNOFFALARM;  // mt-noop(?)
00180   switch (timeout->timeout_info.exitFlag) {
00181   case STILL_WAITING: /* The call timed out */
00182     PTHREAD_CANCEL(timeout->timeout_info.timedThread);
00183     return TRUE;
00184   case TIMED_OUT:
00185     return TRUE;
00186   case NORMAL_TERMINATION:
00187     return FALSE;
00188   default:
00189     xsb_bug("timed call's exit flag is an unexpected value (%d)", timeout->timeout_info.exitFlag);
00190     return FALSE;
00191   }
00192 }
00193 
00194 #else /* NOT MULTITHREADED */
00195 
00196 #ifdef WIN_NT
00197 VOID CALLBACK xsb_timer_handler(HWND wind, UINT msg, UINT eventid, DWORD time)
00198 {
00199   if (exitFlag == STILL_WAITING)
00200     exitFlag = TIMED_OUT; /* tell the timed thread to quit */
00201   TerminateThread((HANDLE)timedThread, 1);
00202 }
00203 
00204 /* message_pump is also known as OP_TIMED_OUT (when WIN_NT is defined) */
00205 int message_pump()
00206 {
00207   MSG msg;
00208   if ((xsb_timer_id = SetTimer(NULL,
00209                                0,
00210                                /* set timeout period */
00211                                (UINT)((int)pflags[SYS_TIMER] * 1000),
00212                                (TIMERPROC)xsb_timer_handler))
00213       == 0) {
00214     xsb_error("SOCKET_REQUEST: Can't create timer: %d\n", GetLastError());
00215     return TIMER_SETUP_ERR;
00216   }
00217 
00218   exitFlag=STILL_WAITING;
00219   while ((exitFlag==STILL_WAITING) && GetMessage(&msg,NULL,0,0)) {
00220     DispatchMessage(&msg);
00221     if (msg.wParam == NORMAL_TERMINATION)
00222       break;
00223   }
00224 
00225   if (xsb_timer_id != 0)
00226     TURNOFFALARM;
00227 
00228   if (exitFlag == TIMED_OUT) 
00229     return TRUE;  /* timed out */
00230   else
00231     return FALSE;  /* not timed out */
00232 }
00233 
00234 #else  /* UNIX */
00235 
00236 /* SIGALRM handler for controlling time outs */
00237 void xsb_timer_handler(int signo)
00238 { 
00239   siglongjmp(xsb_timer_env,1);
00240 }
00241 
00242 #endif
00243 
00244 #endif /* MULTI_THREAD */
00245 
00246 /* the following function is a general format for timeout control. it takes 
00247    function calls which need timeout control as argument and controls the
00248    timeout for different platform */ 
00249 int make_timed_call(CTXTdeclc xsbTimeout *pptr, void (*fptr)(xsbTimeout *))
00250 {
00251 #if defined(WIN_NT) || defined(MULTI_THREAD)   
00252   int return_msg; /* message_pump() return value */
00253 #endif
00254 
00255 #ifdef MULTI_THREAD /* USE PTHREADS */
00256 
00257 #ifdef WIN_NT
00258   pptr->timeout_info.timedThread = mem_alloc(sizeof(pthread_t),LEAK_SPACE);
00259 #define TIMED_THREAD_CREATE_ARG pptr->timeout_info.timedThread
00260 #else
00261 #define TIMED_THREAD_CREATE_ARG &pptr->timeout_info.timedThread
00262 #endif
00263   pptr->timeout_info.th=th;
00264   // below, fptr is pointer to start routine, pptr is pointer to arg-array.
00265   // TIMED_THREAD_CREATE_ARG is a cell of timeout_info.
00266   if (pthread_create(TIMED_THREAD_CREATE_ARG, NULL, fptr, pptr)) {
00267     xsb_error("SOCKET_REQUEST: Can't create concurrent timer thread\n");
00268     return TIMER_SETUP_ERR;
00269   }
00270   PTHREAD_DETACH(pptr->timeout_info.timedThread);
00271   return_msg = OP_TIMED_OUT(pptr);
00272 #ifdef WIN_NT
00273   mem_dealloc(pptr->timeout_info.timedThread,sizeof(pthread_t),LEAK_SPACE);
00274 #endif
00275   if (return_msg == TIMER_SETUP_ERR) {
00276     return TIMER_SETUP_ERR;  
00277   } else if (!return_msg) { /* no timeout */
00278     TURNOFFALARM;
00279     return FALSE;
00280   } else { /* timeout */
00281     TURNOFFALARM;
00282     return TRUE;
00283   }
00284 
00285 #else /* not multithreaded */
00286 
00287 #ifdef WIN_NT
00288   /* create a concurrent timed thread; 
00289      pptr points to the procedure to be timed */  
00290   pptr->timeout_info.parent_thread = (long)GetCurrentThreadId();
00291   if((timedThread = _beginthread(fptr,0,(void*)(pptr)))==-1) { 
00292     xsb_error("SOCKET_REQUEST: Can't create concurrent timer thread\n");
00293     return TIMER_SETUP_ERR;
00294   }
00295   return_msg = OP_TIMED_OUT(pptr);
00296   /* OP_TIMED_OUT returns TRUE/FALSE/TIMER_SETUP_ERR */
00297   if (return_msg == TIMER_SETUP_ERR) {
00298     return TIMER_SETUP_ERR;  
00299   } else if (!return_msg) { /* no timeout */
00300     TURNOFFALARM;
00301     return FALSE;
00302   } else { /* timeout */
00303     TURNOFFALARM;
00304     return TRUE;
00305   }
00306 #else /* UNIX */
00307   SETALARM;     /* specify the timer handler in Unix;
00308                    Noop in Windows (done in SetTimer) */
00309 
00310   if ( !OP_TIMED_OUT ) { /* no timeout */
00311     SET_TIMER; /* specify the timeout period */       
00312     (*fptr)(CTXTc pptr); /* procedure call that needs timeout control */
00313     TURNOFFALARM;
00314     return FALSE;
00315   } else {  /* timeout */
00316     TURNOFFALARM;
00317     return TRUE;
00318   }
00319 
00320 #endif
00321 
00322 #endif
00323 
00324 }

Generated on Wed Jul 26 13:30:42 2006 for XSB by  doxygen 1.4.5