Created
June 9, 2010 14:49
-
-
Save Zoxc/431580 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdbool.h> | |
/* | |
* Dummy typedefs for types used. | |
*/ | |
typedef int event_t; | |
typedef int message_t; | |
/* | |
* Prototype for event functions. | |
*/ | |
void event_clear(event_t event); | |
void event_set(event_t event); | |
void event_wait(event_t event); | |
/* | |
* A function that ensures compiler and hardware read/write order, also known as a memory barrier. | |
*/ | |
inline void memory_barrier() | |
{ | |
__sync_synchronize(); | |
} | |
/* | |
* Portal is an implementation of a pipe where two threads can safely write and read from. It does not support multiple writers or readers. | |
*/ | |
/* | |
* Part is a structure belonging to one of the threads. | |
*/ | |
struct part { | |
/* | |
* The number of writes the other part has done. | |
*/ | |
size_t write_count; | |
/* | |
* The number of reads this part has done. | |
*/ | |
size_t read_count; | |
/* | |
* An event which if preset will be used to signal new messages. | |
*/ | |
event_t event; | |
/* | |
* An event which if preset will be used to signal a reply to a synchronious message. | |
*/ | |
event_t msg_event; | |
/* | |
* The id of the send synchronious message. | |
*/ | |
size_t msg_id; | |
/* | |
* A buffer containg messages to be read. | |
*/ | |
message_t buffer[]; | |
}; | |
struct portal { | |
/* | |
* The part belonging to the other thread. | |
*/ | |
struct part *remote; | |
/* | |
* The part belonging to the local thread. | |
*/ | |
struct part *local; | |
/* | |
* Event used for synchronizing. | |
*/ | |
event_t event; | |
}; | |
/* | |
* part_write() | |
* Write a message to a part. | |
*/ | |
void part_write(struct part *part, message_t msg) | |
{ | |
part->buffer[part->write_count] = msg; | |
memory_barrier(); | |
part->write_count++; | |
memory_barrier(); | |
} | |
/* | |
* part_read() | |
* Read a message from a part. | |
*/ | |
message_t part_read(struct part *part) | |
{ | |
message_t msg = part->buffer[part->read_count]; | |
memory_barrier(); | |
part->read_count++; | |
return msg; | |
} | |
/* | |
* part_pending() | |
* Check if there is pending messages in the part. | |
*/ | |
message_t part_pending_msgs(struct part *part) | |
{ | |
return part->read_count == part->write_count; | |
} | |
void portal_write(struct portal *portal, message_t msg) | |
{ | |
part_write(portal->remote, msg); | |
} | |
void portal_write_and_notify(struct portal *portal, message_t msg) | |
{ | |
part_write(portal->remote, msg); | |
/* | |
* Check if there is a pending waiting event, if so, trigger it. | |
*/ | |
event_t event = portal->remote->event; | |
if(event) | |
event_set(event); | |
} | |
void portal_notify(struct portal *portal) | |
{ | |
/* | |
* Check if there is a pending waiting event and waiting messages, if so, trigger it. | |
*/ | |
event_t event = portal->remote->event; | |
if(event && part_pending_msgs(portal->remote)) | |
event_set(event); | |
} | |
void portal_wait(struct portal *portal) | |
{ | |
/* | |
* Notify the other part if needed before waiting. | |
*/ | |
portal_notify(portal); | |
/* | |
* Setup the waiting event. | |
*/ | |
event_clear(portal->event); | |
portal->local->event = portal->event; | |
/* | |
* Make sure there are no new messages, then wait. | |
*/ | |
if(!part_pending_msgs(portal->local)) | |
event_wait(portal->event); | |
/* | |
* Clear the waiting event. | |
*/ | |
portal->local->event = 0; | |
} | |
bool portal_read(struct portal *portal, message_t *msg) | |
{ | |
/* | |
* Make sure there are new messages. | |
*/ | |
if(!part_pending_msgs(portal->local)) | |
return false; | |
/* | |
* Store the message and return. | |
*/ | |
*msg = part_read(portal->local); | |
return true; | |
} | |
void portal_sync_query(struct portal *portal, message_t msg, size_t msg_id) | |
{ | |
/* | |
* Setup the message waiting event. | |
*/ | |
portal->local->msg_id = 0; | |
memory_barrier(); | |
event_clear(portal->event); | |
portal->local->msg_event = portal->event; | |
/* | |
* Send the message and wait for the reply. | |
*/ | |
portal_write_and_notify(portal, msg); | |
event_wait(portal->event); | |
/* | |
* Clear the message waiting event. | |
*/ | |
portal->local->msg_event = 0; | |
} | |
void portal_sync_reply(struct portal *portal, message_t msg, size_t msg_id) | |
{ | |
/* | |
* Check if the other side is waiting for this message. | |
*/ | |
event_t msg_event = portal->remote->msg_event; | |
memory_barrier(); | |
size_t remote_msg_id = portal->remote->msg_id; | |
if(msg_event && remote_msg_id == msg_id) | |
{ | |
/* | |
* Send the message and trigger the event. | |
*/ | |
part_write(portal->remote, msg); | |
event_set(msg_event); | |
} | |
else | |
{ | |
/* | |
* The other side is not waiting for this message, send it using the regular notify method. | |
*/ | |
portal_write_and_notify(portal, msg); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment