
// The file contains functions used by forked processes and by threads.
// The file is included by ProcessTemplate.c for processes and by 
// SupervisorTemplate.c for threads.

#ifndef SPCOMMON
#define SPCOMMON
#include<errno.h>

// The following type describes data transmitted to a thread when it is created

struct ___thread_data {
  int state;  // initial state of the thread (where the PN token is initially)
  char debug; // 0 if the thread should not run in debug mode
  void* comm; // pointer to unspecified communication structure, such as 
              // "struct ___thread_comm" 
  void* id;   // address of struct proc object of thread in supervisor program
};


struct ___thread_comm {
  // IMPORTANT: Threads may NOT close pipes! 
  // The supervisor and the forked processes are supposed to close them.
  int pread, pwrite; // there are two unidirectional pipes used to communicate with
                     // the supervisor; here are the pipe ends of the thread.
};


enum ___msgType {
  ___EXIT_NOTIFICATION,    // reports that the process/thread will exit
  ___FIRING_REQUEST,       // request for permission to fire
  ___FIRING_NOTIFICATION,  // notifies a transition firing
};

// The following describes the format of the messages sent to the supervisor
// Note: size of struct ___msg is less than PIPE_BUF in order to have atomic
// read/write on the pipe. 
// [ POSIX.1-2001 requires PIPE_BUF to be at least 512 bytes. ]

struct ___msg {
  enum ___msgType type; 
  void* id; // the address of the 'struct proc' object associated with the
            // process in the supervisor code
  int t; // transition number
  int a; // action label of plant transition
  int req_id; // request id; a NONZERO number. If additional information is 
  // communicated to the supervisor about a prev request, it must have the same
  // req_id value as the prev request. Note that this is a request id, not a 
  // notification id. Thus, the variable is not used for notifications.
};


struct ___trans { // used for plant code
  char no_output; // zero if there is an output place
  int place; // output place
  int label; // transition label
  int act; // action number
  int trans; // transition number
};

inline void ___pr_send_signal();
inline void ___th_send_signal();

void ___debugInfoSub(int ___type, int ___id, char* ___vname, char* ___str, va_list ___param);

static inline void debugInfo(char* ___str, ...) {
  va_list ___param;
  if(___debug) {
    va_start(___param, ___str);
#ifdef ___SUPERVISOR
    ___debugInfoSub(0, (int)getpid(), ___NAME, ___str, ___param);
#else
#ifdef ___PROCESS
    ___debugInfoSub(1, (int)getpid(), ___NAME, ___str, ___param);
#else
    ___debugInfoSub(2, (int)pthread_self(), ___NAME, ___str, ___param);
#endif
#endif
    va_end(___param);
  }
}


int ___send_msg(const void* ___pdata, int ___n, int pwrite, \
		void (*dbgfn) (char* ___str, ...));

int ___get_msg(int ___read_end, void *p___ms, int ___n,\
	       void (*dbgfn) (char* ___str, ...));

inline void ___send_exit_notification(struct ___thread_data* ___pthr);

int RequestToFire(int ___n, struct ___trans* ___TRV, \
		  struct ___thread_data *___pthr, \
		  void (*dbgfn) (char* ___str, ...));

void AnnounceFiring(int ___tr, struct ___trans* ___TRV, \
		    struct ___thread_data *___pthr);

int ___NextState(struct ___trans* ___TRV, const char* ___TRVinfo, \
		 int ___cplace, int ___n, int* p___finish, struct \
		 ___thread_data *___pthr, void (*dbgfn) (char* ___str, ...));

void ___TRShuffle(struct ___trans* ___TRV, int ___n);

inline int ___AddToTR(struct ___trans* ___TRV, int ___i, char ___flag, int ___p, \
		      int ___l, int ___t, int ___a);




#ifndef ___THREAD //thread code must not repeat definitions of supervisor code

void ___debugInfoSub(int ___type, int ___id, char* ___vname, char* ___str, va_list ___param) {
  char *___s, *___s2, *___t, *___a; 
  time_t ___currentTime;

  if(!___type)
    ___a = "SUPERVISOR";
  else if(___type == 1)
    ___a = "PROCESS";
  else 
    ___a = "THREAD";

  vasprintf(&___s, ___str, ___param);
  time(&___currentTime);
  ___t = asctime(localtime(&___currentTime));
  if(!___vname)
    asprintf(&___s2, "\n\n%s %d %s%s", ___a, ___id, ___t, ___s);
  else
    asprintf(&___s2, "\n\n%s %s (%d) %s%s", ___a, ___vname, ___id, ___t, ___s);
  fflush(stderr);
  fputs(___s2, stderr);
  free(___s);
  free(___s2);  
}



int ___send_msg(const void* ___pdata, int ___n, int pwrite, \
		void (*dbgfn) (char* ___str, ...)) {
  // Sends ___n bytes of data located at address ___pdata. 
  // It returns 0 if the message could not be sent.  

  int ___ret;
  ___ret = write(pwrite, ___pdata, ___n);
  if(___ret < ___n && ___ret >= 0) {
    dbgfn("WRITE() has written only %d out of %d bytes", ___ret, ___n);
    return 0;
  }
  if(___ret == -1) {
    if(errno == EINTR) { // try again
      ___ret = write(pwrite, ___pdata, ___n);
      if(___ret != ___n) {
	dbgfn("Unable to send message");
	return 0;
      }
    }
    else {
      dbgfn("WRITE() has returned the error code %d", errno);
      return 0;
    }
  }

  // Notify supervisor that a message has been sent

#ifdef ___PROCESS
  ___pr_send_signal();
#else
  ___th_send_signal();
#endif

  //if(write(pwrite, ___pdata, ___n) == -1)
  //  return 0;

  return 1;
}


int ___get_msg(int ___read_end, void *p___ms, int ___n,\
	       void (*dbgfn) (char* ___str, ...)) {
  // int ___check_for_msg(struct proc *___p, struct ___msg *p___ms) {
  // The function attempts to read ___n bytes of data and write them 
  // to p___ms. 
  // The function returns 0 if there are no messages and read calls are not
  // blocking (such as in the case of the supervisor).

  int ___ret, ___t;

  if(___n <= 0) return 0;

  //dbgfn("Inside get_msg");
  ___ret = read(___read_end, p___ms, ___n);
  //if(___ret == -1 || ___ret == 0) // -1 indicates error
  //  return 0;
  if(___ret <= 0) {
    // ignore a common condition: nonblocking read from empty pipe
    if(___ret == -1 && errno == EAGAIN)
      return 0;
    // report other conditions  
    dbgfn("read() has returned %d in ___get_msg(); errno = %d", ___ret, (int)errno);
    return 0;
  }
  if(___ret < ___n) { // Try to recover the remaining bytes
    dbgfn("read() has returned %d out of %d bytes in ___get_msg()",\
	      ___ret, ___n);
    for(___t = ___ret; ___t < ___n; ___t += ___ret) {
      ___ret = read(___read_end, ((char*)p___ms)+___t, ___n - ___t);
      if(___ret == -1 && errno != EINTR)
	break;
      else if(___ret <= 0)
	break;
    }
    if(___t != ___n) {
      // This line should not be reached since write() of n bytes is atomic 
      dbgfn("UNABLE TO RECOVER FROM READ() ERROR. SUBSEQUENT READS FROM THIS PIPE MAY BE CORRUPTED.");
      return 0;
    }
  }

  return 1;
}


inline void ___send_exit_notification(struct ___thread_data* ___pthr) {
  struct ___msg ___ms;
  ___ms.type = ___EXIT_NOTIFICATION;
  ___ms.id = ___pthr->id; 
  //___send_msg(&___ms, sizeof(___ms), ((struct thread_comm*) x)->pwrite);
  ___send_msg(&___ms, sizeof(___ms), ((struct ___thread_comm*) (___pthr->comm))->pwrite, debugInfo);
}


#ifdef ___SUPERVISOR
static pthread_mutex_t ___mtx = PTHREAD_MUTEX_INITIALIZER;
#endif


int RequestToFire(int ___n, struct ___trans* ___TRV, \
		  struct ___thread_data *___pthr, \
		  void (*dbgfn) (char* ___str, ...)) {
  // The function considers the following list of transitions
  //  ___TR[0] ... ___TR[n-1]
  // The function returns an integer ts >= 0, where ___TR[ts]
  // is the transition that is given permission to fire. 
  // The select() function or its equivalent should be called
  // before this function. The select() function creates the
  // __TR list of n elements. 

  int ___ts = 0, ___i, ___j;
  static int ___req_id = 0;
  struct ___msg ___ms;

#ifdef ___SUPERVISOR
  pthread_mutex_lock(&___mtx); // one thread at a time may change ___req_id
  // The lock: a temorary solution. It would be better to assigne a ___req_id
  // to each thread and avoid in this way the mutex. There is no need to have
  // a unique ___req_id for all threads. 
#endif

  ___req_id++;
  if(!___req_id)
    ___req_id = 1;

#ifdef ___SUPERVISOR
  pthread_mutex_unlock(&___mtx);
#endif

  ___ms.type = ___FIRING_REQUEST;
  ___ms.id = ___pthr->id;
  ___ms.req_id = ___req_id;

  for(___i = 0; ___i < ___n; ___i++) {
    ___ms.t = ___TRV[___i].trans;
    ___ms.a =  ___TRV[___i].act;
    ___j = ___send_msg(&___ms, sizeof(___ms), \
		    ((struct ___thread_comm*) (___pthr->comm))->pwrite, dbgfn);
    if(!___j)
      dbgfn("Unable to send firing request to supervisor");
  }
  
  // Read is blocking; wait until permission is granted to fire some transition

  dbgfn("Waiting for request approval ... %d %d %d", (int)(long)___pthr, (int)(long)___pthr->comm, (int)((struct ___thread_comm*) (___pthr->comm))->pread); // (long) used to avoid a gcc warning on 64 bit systems

  ___j = ___get_msg(((struct ___thread_comm*) (___pthr->comm))->pread,	\
		    &___ts, sizeof(___ts), dbgfn);
  if(!___j)
    dbgfn("Error while waiting for permission to fire");
  dbgfn("Request approved");

  // The following loop could be removed if in SupervisorTemplate the field
  // 'fireable' of 'struct proc' is set to the index of the transition, not
  // to the transition itself. Then, that index would be sent and there would
  // be no need to determine the index in the following loop. Note that sending
  // the index would assume that the supervisor receives the messages of the 
  // loop above in the same order in which they are sent. This would be the 
  // case when pipes are used for interprocess communication.  

  for(___i = 0; ___i < ___n; ___i++) 
    if (___ts == ___TRV[___i].trans)
      return ___i;
  dbgfn("Internal ERROR: The supervisor enabled transition t%d is unavailable; %d transitions were proposed, the first being t%d", ___ts, ___n, ___TRV[0].trans);
  return 0;
  //  return ___ts;
}


void AnnounceFiring(int ___tr, struct ___trans* ___TRV, \
		    struct ___thread_data *___pthr) {
  // ___tr is the index of the transition in the ___TR list that is fired
  
  struct ___msg ___ms;
  ___ms.type = ___FIRING_NOTIFICATION;
  ___ms.t = ___TRV[___tr].trans;
  ___ms.a =  ___TRV[___tr].act;
  ___ms.id = ___pthr->id;
  ___send_msg(&___ms, sizeof(___ms), ((struct ___thread_comm*) (___pthr->comm))->pwrite, debugInfo);
}


int ___NextState(struct ___trans* ___TRV, const char* ___TRVinfo, \
		 int ___cplace, int ___n, int* p___finish, struct \
		 ___thread_data *___pthr, void (*dbgfn) (char* ___str, ...)) {
  // ___n: the number of items in the ___TR list.
  // ___clplace: value of current place
  // Returns the next place.

  int ___t, ___i; 

  ___i = 0;
  ___t = ___TRV[0].trans; 
  if(___TRVinfo[___t] & 5) { // then the transition is controlled
    ___i = RequestToFire(___n, ___TRV, ___pthr, dbgfn);
    ___t = ___TRV[___i].trans;
  }
  else if(___TRVinfo[___t] & 10) // then the transition is observed
    AnnounceFiring(___i, ___TRV, ___pthr);

  *p___finish = ___TRV[___i].no_output;

  if(*p___finish)
    dbgfn("Terminating by means of transition %d from place %d ...",\
	      ___t, ___cplace);
  else 
    dbgfn("Firing transition %d from place %d to place %d ...",\
	      ___t, ___cplace, ___TRV[___i].place);  

  return ___TRV[___i].place;
}




void ___TRShuffle(struct ___trans* ___TRV, int ___n) {
  // ___n: the number of items in the ___TR list.
  // The function orders the list in random order. The reason is that
  // the likelihood of getting a transition firing approved increases
  // with the rank in the list (first element has highest priority).
  // Thus, this function helps ensure fairness.
 
  int ___i, ___j;
  struct ___trans ___tmp;

  for(___i = ___n; ___i > 1; ) {
    ___j = rand()%___i;
    ___i--;
    if(___j == ___i)
      continue;
    ___tmp = ___TRV[___i];
    ___TRV[___i] = ___TRV[___j];
    ___TRV[___j] = ___tmp;
  }
}



inline int ___AddToTR(struct ___trans* ___TRV, int ___i, char ___flag, int ___p, \
		      int ___l, int ___t, int ___a) {

  ___TRV[___i].no_output = ___flag;
  ___TRV[___i].place = ___p;
  ___TRV[___i].label = ___l;
  ___TRV[___i].trans = ___t;
  ___TRV[___i].act = ___a;
  ___i++;
  return ___i;
}


#endif //#ifndef ___THREAD

#endif //#ifdef SPCOMMON
