/* mp.h
   message-passing library for CS 537 project 2
   
   You are required to implement the functions declared in this file. */


#ifndef SEEN_MP_H
#define SEEN_MP_H 1



/* Type and constant definitions -- you may not modify any of these! */


/* An entity may send an empty message with tag type TAG_DONE to indicate 
   that it has completed its work.  If you follow this convention, the
   equivalent of
   
      join(id);
      
   would be
   
      mp_recv(id, TAG_DONE);
   
 */

#define TAG_DONE 1

#define MAIN_ENTITY 0
#define ANY_SENDER -1
#define ANY_TAG -1

typedef int entity_t;  /*  a communicating entity  */
typedef int msgtag_t;  /*  a message tag  */

typedef struct {
	entity_t sender;  /* the entity sending this message */
	entity_t recipient;  /* the entity to whom this message is addressed */
	msgtag_t tag;   /* the "tag" ascribed to this message */
	unsigned size;  /* the size of the data associated with this message */
	void* payload;  /* the data payload of this message, if any */
} message_t;  /* a message */



/* Function declarations -- you will have to implement functions 
   with the following behaviors; DO NOT modify the declarations */


/* mp_next_id returns the next available entity ID.  The first time you 
   call this function, it should return 1 (since 0 is the entity ID for 
   the "main thread"); the next, it should return 2; and so on. (Hint:  
   use a "static" variable) 
   
   Note:  I have provided an implementation of this function for you. */

int mp_next_id();

/* mp_new_entity takes one parameter; an entity identifier.
   If id corresponds to a preexisting entity, mp_new_entity does 
   nothing and returns -1 to indicate error.  Otherwise, it initializes 
   any data structures (mailboxes, etc.) necessary for a new entity 
   with identifier "id" to communicate with other entities, returning 0 
   on success. 
   
   Note that the first time you call mp_new_entity, it should also construct 
   a mailbox with id 0, for the "main thread."
   */

entity_t mp_new_entity(entity_t id);

/* mp_create takes five parameters, corresponding to the elements
   of message_t and returns a newly-allocated message_t structure with 
   the provided values filled-in.  Your mp_send function should call this
   function.  */

message_t* mp_create(entity_t sender, entity_t recipient, msgtag_t tag,
					 unsigned size, void* payload);


/* mp_send constructs a new message with the specified recipient, tag, 
   size, and payload, and places it in the recipient's mailbox.  If the 
   recipient's mailbox is full, mp_send blocks until the message can be
   delivered. */
   
int mp_send(entity_t recipient, msgtag_t tag, unsigned size, void* payload);

/* The behavior of mp_recv depends on the values of sender and tag.  

   1. If sender and tag are both set to "-1" (ANY_SENDER and ANY_TAG), then
      mp_recv will return the first available message in the caller's 
      mailbox, removing that message from the mailbox.  If there are no 
      available messages, mp_recv will block.
   2. If sender is set to some value other than ANY_SENDER, then mp_recv will
      return the first available message from "sender" in the caller's mailbox.
      If there is no such message, mp_recv will block.
   3. If tag is set to some value other than ANY_TAG, then mp_recv will
      return the first available message with tag "tag" in the caller's mailbox.
      If there is no such message, mp_recv will block.
   4. If both tag and sender are set, then mp_recv will return the first
      available message satisfying both constraints, blocking if no such message 
      is available.
*/
   
message_t* mp_recv(entity_t sender, msgtag_t tag);

/* The arguments to mp_poll are to be interpreted in the same way as those 
   to mp_recv, but mp_poll never blocks.  Rather, it returns 1 if a call to 
   mp_recv with the same arguments WOULD NOT BLOCK, and 0 if a call to 
   mp_recv with the same arguments WOULD BLOCK. */

int mp_poll(entity_t sender, msgtag_t tag);

/* 
 mp_new_thread should create a new thread with the given entity ID, 
 executing the given function with the given argument.  Your thread 
 function should NOT call pthread_exit -- it should simply return.
 
 Note that I have provided an implementation of mp_new_thread for you.
 */

int mp_new_thread(entity_t entity_id, void * (*func)(void *), void * arg);

#endif /* SEEN_MP_H */

