// mc3_svc.c - Skeleton to invoke the remote procedures

/*
 * This file was generated using rpcgen, then edited to suport
 * one thread per request and locks to guarantee mutual exclusion
 * when accesing shared data.
 */

#include <stdio.h>
#include <rpc/rpc.h>
#include <pthread.h>
#include "mc.h"

static void mcprog_1();

pthread_mutex_t mutex;      /* Used by threads to implement Critical */
                            /* Regions */

int
main()
{
  register SVCXPRT *transp;
  
  (void) pmap_unset(MCPROG, MCVERS);
  transp = svctcp_create(RPC_ANYSOCK, 0, 0);
  if (transp == 0) {
    fprintf(stderr, "cannot create tcp service.");
    exit(1);
  }
  if (!svc_register(transp, MCPROG, MCVERS, mcprog_1, IPPROTO_TCP)) {
    fprintf(stderr, "unable to register (MCPROG, MCVERS, tcp).");
    exit(1);
  }
  
  /* Create a mutex */
  if (pthread_mutex_init(&mutex, pthread_mutexattr_default)) {
    perror("pthread_mutex_init");
    exit(1);}
  
  svc_run(); /* This thread waits here until there is a call from
	      * a client. When there is a call mcprog_1 is called
	      * automatically. When mcprog-1 returns, this thread
	      * waits again in svc_run.
	      */
  
  fprintf(stderr, "svc_run returned"); /*It should not happen.*/
  exit(1);
  /* NOTREACHED */
  return 0;
}

typedef struct {           /* Descriptor of a request from client */
  pthread_t *threadp;      /* Pointer to thread that will handle a request */
                           /* [There may be multiple active threads. Only */
                           /* last is kept track of.*/
  union {
    mypair add_1_arg;
    mypair subtract_1_arg;
  } argument;
  char *result;
  char *(*local)();
  struct svc_req *rqstp;
  SVCXPRT *transp;
  } request;

static void handle(request * b); /* Function executed by thread */

/* mcprog_1 is invoked each time that there is a remote procedure
 * call from the client. It creates a request structure with
 * information about the transport used for the request, the
 * request received, the arguments of the call. It creates a
 * detached thread to services this request, and it gets out of 
 * the way. Thus the server becomes ready to accept another call
 * from a client while the threads take care of the previous
 * client requests.
 */
static void
mcprog_1(struct svc_req *rqstp, register SVCXPRT *transp)
{
  request * requestp;

  /*
   * Allocate request structure
   */
  if ((requestp = (request *)malloc(sizeof(request))) == 0) {
    perror("Malloc request"); exit(1);}

  requestp->transp = transp;
  requestp->rqstp = rqstp;

  bzero((char *)&(requestp->argument), sizeof(requestp->argument));
  if (!svc_getargs(transp, (xdrproc_t)xdr_mypair, 
		   (char *)&(requestp->argument))) {
    svcerr_decode(transp);
    return;
  }

  /*create thread */
  if ((requestp->threadp = (pthread_t *)malloc(sizeof(pthread_t))) == 0) {
    perror("Malloc pthread_t"); exit(1);}
  if (pthread_create(requestp->threadp, pthread_attr_default, (void *) handle, 
		     requestp) != 0) {
    perror("pthread_create"); exit(1);}
  //printf("thread created\n");

  /* Code to reclaim the space allocated for thread objects. */
  if (pthread_detach(requestp->threadp) == 0) {
    //printf("pthread detached\n");
  } else {
    perror("pthread_detach error"); exit(1);}
}


static void handle( request * b) /* code executed by a thread */
{

  switch (b->rqstp->rq_proc) {
  case NULLPROC:
    (void) svc_sendreply(b->transp, (xdrproc_t)xdr_void, (char *)NULL);
  return;
  
  case ADD:
    b->local = (char *(*)()) add_1;
    break;
    
  case SUBTRACT:
    b->local = (char *(*)()) subtract_1;
    break;
    
  default:
    svcerr_noproc(b->transp);
    return;
  }

  if (pthread_mutex_lock(&mutex)) {
    perror("pthread_mutex_lock");
    exit(1);}
  sleep(2); // Hack used to increase duration of critical region
  (b->result) = (*(b->local))(&(b->argument), b->rqstp);
  if (pthread_mutex_unlock(&mutex)) {
    perror("pthread_mutex_unlock");
    exit(1);}

  if ((b->result) != NULL 
      && !svc_sendreply(b->transp, (xdrproc_t)xdr_int, (char *)b->result)) {
    svcerr_systemerr(b->transp);
  }

  if (!svc_freeargs(b->transp, (xdrproc_t)xdr_mypair, 
		    (char *)&(b->argument))) {
    fprintf(stderr, "unable to free arguments");
    exit(1);
  }
  
  /* Prevent memory leak */
  free(b->threadp);
  free(b);

  return;
}
