What does SystemCycle() do ?

======================================
| syslib.c : _SystemCycle(int chkcmd)|
======================================

n.b. macro in tinetype.h :

#ifndef SystemCycle
#define SystemCycle(i) _SystemCycle(i)
#endif

=> can alway #define SystemCycle to do 'additional things'

TINE_EXPORT int _SystemCycle(int chkcmd)
{
  int nullpass = TRUE;
  ProbeTimeElapsed("SystemCycle entered");
# ifdef MULTITHREADED
  if (!isAllowedSystemCycle()) { millisleep(gSystemTick); return 0; }
  if (!cycle_priority_fixed) fixSystemCyclePriority();
# endif
/// can add stdin to the descriptor set IF in foreground ...
# ifdef UNIX
  foregroundTTY = chkcmd ? IsInForeground() : FALSE;
# endif
# ifdef MULTITHREADED
  if (gRunServerCycleInSeparateThread)  /// <<< default = TRUE
  {
    if (gNeedToStartSrvCycleThread) srvCycleCreateThread(0);  /// <<< launch serverCycle() in its own thread
    if (gIsRunningAsServer && !gInitTineServicesDone) millisleep(1);
  }
  else
# endif
  {
    if ((gEqmListSize > 0) || FecNameRegistered || gDelayInitTineServices) 
    { /* only run thru server cycle if it makes sense */
      serverCycle();  /// <<< same thread !!!
      nullpass = FALSE;
    }
  }
  checkTasklist(); /* run through the task list */   /// <<< any background tasks ???
  if (nConnectionTableEntries || glbClnSck) 
  { /* only run thru client cycle if it makes sense */
    clientCycle();   /// <<< only do this if there are active client connection entries ...
    nullpass = FALSE;
  }
# ifdef RDATE
  if (SychronizeFecClock) FecSynchronize(); /* Synchronize FEC */  /// <<< was used in MSDOS & VxWorks; doesn't seem to be relevant any more
# endif
# ifdef DOS
  if (watchdogBaseAddress) watchDog(); /* hardware watchdog */  /// <<< time to get rid of this 
# endif
# if defined(DOS) || defined(WIN32) || defined(NIOS)
  if (chkcmd) GetCommand(); /* simple command line interpreter */  /// <<< stdin not a FD in windows !  
# endif
# ifndef SMALL_TINE_LIB
  if (_gNeedWarmRestart) _SystemReInit();  /// <<< if 'true' restart everything as gracefully as possible
# endif
  if (nullpass) millisleep(gSystemTick);
  ProbeTimeElapsed("SystemCycle returning");
  if (!gIsRunningAsServer && ttimeprobe > 0) ttimeprobe--;
  return (ReturnToServerCycle | ReturnToClientCycle); /* need to return */
  /// clientCycle() and serverCycle() will both 'check' for bytes to read on relevant sockets.
  /// IF something is acted upon AND there's still 'more to do' then we can't poll() sockets with a timeout > 0 !
}


============================
| server.c : serverCycle() |
============================

/// primary duties: 
/// - check if client-side requests are coming in (AcceptRequest())
/// - check if contracts need to be filled or data needs to be sent to clients -> call the scheduler! (doScheduler())
/// - time to send globals (if we're sending globals) ? (doGCast())
/// - check for alarms (SurveyAlarms())
/// - access local histories (if any) historyCycle()

int serverCycle(void)
{
  struct timeval tv;
  static struct timeval lastCycleTime;
  static int CycleCounts = 0;

  if (ClientListCapacity <= 0) return 0; /* no server functionality required */
  ProbeTimeElapsed("server Cycle entered");
  if (ServerExitCondition) 
  { 
    gServerCycleState = CYCLE_STATE_EXIT;
    if (ServerExitCondition++ > 3 && gHaltOnCycleHalt && !gIsExitingEQPModules) haltServer(); 
  }
  if (!ServerInitialized) return 0; /* no server functionality required yet */
  if (gServerCycleIdle)
  {
    gServerCycleState = CYCLE_STATE_IDLE;
    AcceptRequest(&thisCycleTime);/* accept console commands */
    return 0;
  }
  if (!gLastMinuteInitializationDone) 
  {
    int lmi_done = FALSE;
    gServerCycleState = CYCLE_STATE_INIT_FINAL;
    if (WaitForSystemMutex(hSystemInitMutex,gSystemTick) == 0)
    {
      gettimeofday(&thisCycleTime,(struct timezone *)NULL);/* Cycle time */
      lmi_done = LastMinuteInitialization();     
	  /// <<<< initializes equipment modules (calls the 'ini' routines
	  /// <<<< the 'restore' part of save-and-restore
	  /// <<<< failover monitor needed ?
      lastCycleTime = thisCycleTime;
      ReleaseSystemMutex(hSystemInitMutex);
    }
    if (!gSystemRunningStandAlone) 
    {
      if (useGlobalSynchronization) SystemStartGlobalSynchronization();  
	  /// <<< sync data timestamps as per TINE TIMESERVER (default TRUE) 
#     ifndef SMALL_TINE_LIB
      if (useCycleTrigger) SystemStartCycleTrigger();  
	  /// <<< acquire cycle/event number as per TINE CYCLER (default TRUE)
#     endif
    }
    gLastMinuteInitializationDone = lmi_done;
  }
  if (WaitForSystemMutex(hSystemServerMutex,0) != 0) return not_ready;
  gettimeofday(&thisCycleTime,(struct timezone *)NULL);/* Cycle time */
  lastCycleTime = thisCycleTime;
  if (!gInitTineServicesDone) 
  { /* no server functionality required yet */
    ReleaseSystemMutex(hSystemServerMutex);
    return 0; 
  }
# ifndef SMALL_TINE_LIB
  if ((gSystemDataStampDly > 0) && /* is a delay value registered */
      (gSystemDataStamp != gSystemDataStampRb) && /* system stamp not yet applied */
      MSECS(thisCycleTime,gSystemDataStampTs) > gSystemDataStampDly) /* the delay period has elapsed */
  { /* apply system stamp */
    applySystemDataStamp();
  }
# endif
  gServerCycleNeedsScheduler = gSchedulerInterval > 0 ? TRUE : FALSE; /* turned on during the 'accept'! */
  ProbeTimeElapsed("server Cycle checking requests");
  tv = thisCycleTime; /* note this, it might change ! */
  gServerCycleState = CYCLE_STATE_ACCEPT;
  AcceptRequest(&thisCycleTime);/* accept consumer subscriptions and renewals   */
  /// <<<< 'AcceptRequest' is a macro that will call AcceptIp() and on windows : DoServerEvents() 
  /// <<<< this can 'block' for up to a 'gSystemCycleDeadband' (default : 10 msec)
  /// <<<< unless (ReturnToServerCycle | ReturnToClientCycle) is 'TRUE'
  gTimeServerIdleLastCycle += gTimeFdIdle;
  ProbeTimeElapsed("server Cycle finished checking requests");
  if (gSchedulerInterval <= 0 || MSECS(tv,lastScheduled) >= gSchedulerInterval)
  { /* is it time to service consumers? */
    if (MSECS(tv,lastScheduled) > (int)gSystemCycleDeadband) gServerCycleNeedsScheduler = TRUE;
    if (gServerCycleNeedsScheduler) doScheduler(&thisCycleTime,gSystemTick,SCHED_INTERN,NULL,NULL);
  }
  if (ngcasts) doGCast(&thisCycleTime);
  if (acquireDeviceNamesFromProperties) 
  {
    /// .NAM property information can registered (the best way) OR the <property>.NAM can be registered
	/// in which case, the tine kernel needs to ASK the equipment module for the information ...
    gServerCycleState = CYCLE_STATE_ACQNAMES;
    devNamesInitialization();
  }
  gServerCycleState = CYCLE_STATE_CLEANUP;
  CleanupTransfers(&thisCycleTime);
  /// <<<< cleans up any 'stranded' connection table entries, etc. (sanity check)
  ProbeTimeElapsed("server Cycle finished checking transfer cleanup");
  SurveyAlarms(&thisCycleTime);
  /// <<<< check for alarm information changes
  ProbeTimeElapsed("server Cycle finished checking alarms");
  if (nHistoryRecords) 
  {
    historyCycle(gHistoryCycleNeedsScheduler);
	/// <<<< local history data acquisition
    ProbeTimeElapsed("server Cycle finished checking history");
  }
  if (!ExportsRegistered) 
  {
    gServerCycleState = CYCLE_STATE_SNDEXP;
    sndRegisteredExports(&thisCycleTime);
    /// <<<< sends info to the ENS 
  }
  gServerCycleState = CYCLE_STATE_CHK_LOCKS;
  checkAccessLocks((time_t)thisCycleTime.tv_sec);
  /// <<<< are there active access locks ???
  gServerCycleState = CYCLE_STATE_CHK_GRP;
  checkGroupMembers((time_t)thisCycleTime.tv_sec);
  if (gRespondToServiceRequests) reAttachToServiceGroup();
  if (!gExportManifestsDone && thisCycleTime.tv_sec > gSystemStartupTime + 20)
  {
    dumpExportManifests();
	/// <<< after 20 seconds of up time : dump the export information to a manifest
    gExportManifestsDone = TRUE;
  }
  gTimeServerCycle += MSECS(thisCycleTime,lastCycleTime);
  lastCycleTime = thisCycleTime;
  CycleCounts++;
  if (gTimeServerCycle >= 1000)
  { /* have spent a cummulative 1000 msec in the serverCycle() */
    if (CycleCounts > gMaxCycleCounts) gMaxCycleCounts = CycleCounts;
    gCycleCounts = CycleCounts;
    if (!gIsCyclePollingMode)
    {
      AveBusyTime = (int)((gTimeServerCycle-gTimeServerIdleLastCycle)*100/gTimeServerCycle);
    }
    else
    {
      AveBusyTime = (gMaxCycleCounts-CycleCounts)*100/gMaxCycleCounts;
    }
    gTimeServerCycle = gTimeServerIdleLastCycle = 0;
    CycleCounts = 0;
  }
  ReleaseSystemMutex(hSystemServerMutex);
  ProbeTimeElapsed("server Cycle returning");
  if (ttimeprobe > 0) ttimeprobe--;
  return 0;
}

#define AcceptRequest(tv) { AcceptShm(100); AcceptIP(tv); }
#if defined(MAKE_TINE32_DLL) && !defined(NO_TINE_PIPE)
# define doShmConsumer(n) DoClientEvents(n)
# define AcceptShm(n) DoServerEvents(n)
#else //// <- the non-windows world 
# define doShmConsumer(n) ;
# define AcceptShm(n) ;
#endif

========================
| srvip.c : AcceptIP() |
========================

/// server is listening on 4 sockets (1 UDP, 3 TCP) (potentially 6 if we ever use IPX again) and a pipe and stdin
/// if something comes in ... 
/// it is probably a contract request
/// it could be a connection request (TCP Parent socket)
/// it could be an 'attachfec' request (TCP debug socket or pipe)
/// it could be a foreground request (debug via stdin)

void AcceptIP(struct timeval *tv)
{
  // ...
  /// bare-bones OSes (MSDOS, NIOS) will be in a 'polling mode' => 'we' are the OS and we check everything as fast as we can !
  /// but everyone else can 'block' on a socket select for a 'SystemDelay' time of (default) 10 msec
  /// ...
  if (!gIsCyclePollingMode)
  { /* fill in the rw select()ion set : */
    ltv = *tv;
    nfds = addServerSocketsToSet(fds,nfds,MAX_POLLED_FDS);
    if (!gRunServerCycleInSeparateThread) nfds = addClientSocketsToSet(fds,nfds,MAX_POLLED_FDS);
	/// single threaded (or ServerCycle() and ClientCycle() in same thread : put server and client sockets in the same select/poll pool :
    ....	
retry_select:
    /* anything fall through select() ? */
    if (!gRunServerCycleInSeparateThread) gWaitingOnSelect = TRUE;
    pto = to->tv_sec * 1000 + to->tv_usec/1000;
    nready = poll(fds,nfds,pto);
    if (nready < 0) eno = soerrno;
    if (nready < 0 && eno == EINTR && retry-- > 0)
    {
      to->tv_sec = 0; to->tv_usec = 0;
      goto retry_select;
    }
	/// ---> 'goto' haters can easily re-write this one as a while() ...
	
    /* is there a hook ? */
    if (socketAcceptEntryFcn) (socketAcceptEntryFcn)();
	//// Karol/Steve once needed the above line and did provide an 'accept()' hook function ...
	//// this probably (?) isn't needed any more ... but it doesn't hurt to have it ...

    if (nready < 0) 
    { 
      if (eno == EINTR) return; /* pass on an interrupted i/o */
      rpterr = TRUE;
      neterrcnt++; 
      ccerr(nready); /* serious low-level socket error (errno will be set) */
    }
    if (nready == 0) 
    { /* empty pass : call the scheduler (else trust the consumer logic) */
      gServerCycleNeedsScheduler = TRUE;
      if (tineDebug > 5) dbglog("null request -> call scheduler");
      ccerr(0);
    }

    gSelectOnSysPoll = FALSE;
#   ifdef UNIX
    ... 
	/// stdin shows up as a file descriptor on unix systems ....
	/// so check for 'foreground' input here :
    if (foregroundTTY && fds_isset(STDIN_FILENO) && GetCommandEx(STDIN_FILENO,cmdbuf,CMDSIZE) > 0) 
      InterpretCommand(cmdbuf);
#   ifndef NO_SERVICE_PIPE
    /* attached process issuing a command ? */
	/// unix pipes also show up as file descriptors ...
	/// attachfec in the file descriptor list ?
    chkIpcClnLst(fds,nfds);
#   endif /* NO_SERVICE_PIPE */
#   endif /* UNIX */
    ...
  } /// <<<< end of check on !gIsCyclePollingMode
  
  /* is someone trying to get a connection ? */
  /// TCP : a client is calling 'connect' => the TCP parent socket is 'ready' :
  if (gIsCyclePollingMode || fds_isset(tcpSrvSck))
  {
    p = (gAfInet == AF_INET6) ? TCP6 : TCP;
    if ((i=AssignConnectionSocket(tcpSrvSck,p,&nTCPsck,tcpSckTbl,tcpConTblSize)) >= 0) 
    {
      fds[nfds].fd = tcpSckTbl[i].sck; fds[nfds].events = POLLIN; nfds++;
    }
  }
# ifdef MULTITHREADED
  ///
  /// TCP: that special STREAM socket is getting a connect() request
  /// and this must be a multi-threaded build
  /// (can we ever get rid of this?) 
  if (gIsCyclePollingMode || fds_isset(tcpStrmSck))
  {
    p = (gAfInet == AF_INET6) ? STREAM6 : STREAM;
    if ((i=AssignConnectionSocket(tcpStrmSck,p,&nTCPsck,tcpSckTbl,tcpConTblSize)) >= 0) 
    { 
      fds[nfds].fd = tcpSckTbl[i].sck; fds[nfds].events = POLLIN; nfds++;
    }
  }
# endif
  ///
  /// 'remote' attachfec (via a TCP socket and not a local pipe)
  ///
  if (gAllowRemoteDebugging)
  {
    if (gIsCyclePollingMode || fds_isset(dbgSrvSck))
    {
      p = (gAfInet == AF_INET6) ? DBGPIPE6 : DBGPIPE;
      if ((i=AssignConnectionSocket(dbgSrvSck,p,&nDBGsck,dbgSckTbl,dbgConTblSize)) >= 0) 
      { 
        fds[nfds].fd = dbgSckTbl[i].sck; fds[nfds].events = POLLIN; nfds++;
      }
    }
  }
  /// ...
  ///
  /// if something has come in on a server socket ... then look at it with
  /// getConsumerIPAddress() 
  ///
  /* has something come in on a connected socket ? */
  for (i=0; i<nTCPsck; i++)
  {
    if (gIsCyclePollingMode || fds_isset(tcpSckTbl[i].sck)) 
      getConsumerIPAddress(tcpSckTbl[i].sck,tcpSckTbl[i].type);
  }
  /* has something has come in on a UDP socket ? */
  p = (gAfInet == AF_INET6) ? UDP6 : UDP;
  if (gIsCyclePollingMode || fds_isset(udpSrvSck)) getConsumerIPAddress(udpSrvSck,p);
  /* has something has come in on the service socket ? */
  ///
  /// if the server can respond to 'where are you' requests (i.e. EPICS-like; no ENS services)
  ///
  if (netSrvSck != 0)
  {
    if (gIsCyclePollingMode || fds_isset(netSrvSck)) getConsumerIPAddress(netSrvSck,p);
  }
  ///
  /// attachfec clients ?
  ///
  for (i=0; i<nDBGsck; i++)
  {
    if (gIsCyclePollingMode || fds_isset(dbgSckTbl[i].sck)) chkDbgClnLst(fds,nfds);
  }
err:
  ///
  /// ...
  /// cleanup ...
  ///
  gettimeofday(tv,(struct timezone *)NULL);
  return;
}


============================
| client.c : clientCycle() |
============================

/// primary duties:
/// - check for incoming data from current contracts (CheckConsumerData())
/// - check state of subscriptions (time to renew? etc.) (checkSubscriptions())
/// - check state of connections (timeouts ? etc.) (checkConnections())
/// - check callback notifications (checkClientNotification())

void clientCycle(void)
{
  int saveClientCycle;
  ProbeTimeElapsed("client cycle entered");
  if (gClientCycleIdle)
  {
    millisleep(gSystemTick);
    return;
  }
  if (WaitForSystemMutex(hSystemClientMutex,0) == 0)
  {
    ProbeTimeElapsed("client cycle check activity");
    CheckConsumerData(FALSE); /* flush input on the socket */
    checkSubscriptions(FALSE); /* need to send anything out ? */
    CheckConsumerData(TRUE);  /* check input on socket again */
    if (needToAcknowledge)
    { /* last batch of updates requires an ACK */
      checkSubscriptions(FALSE);
      needToAcknowledge = FALSE;
    }
    checkConnections(); /* missing heartbeats ?   */
    ReleaseSystemMutex(hSystemClientMutex);
  }
# ifdef MULTITHREADED
  else if (gRunServerCycleInSeparateThread)
  { /* nothing else below will yield execution ! */
    YieldThread();
  }
# endif
  ProbeTimeElapsed("client cycle check notifications");
  checkClientNotification(); /* always check, irrespective of Semaphore */
  ProbeTimeElapsed("client cycle check local pipes");
  checkRemotePipe();
# if !defined(SMALL_TINE_LIB)
  if (accessLockLst != NULL) checkAccessLockItems();
# endif
  ProbeTimeElapsed("client cycle returning");
  return;
}


