diff --git a/src/libguac/guacamole/socket.h b/src/libguac/guacamole/socket.h index 5d3e9b69..13768595 100644 --- a/src/libguac/guacamole/socket.h +++ b/src/libguac/guacamole/socket.h @@ -38,6 +38,8 @@ #ifndef _GUAC_SOCKET_H #define _GUAC_SOCKET_H +#include +#include #include #include @@ -187,6 +189,18 @@ struct guac_socket { */ char* __instructionbuf_elementv[64]; + /** + * Whether instructions should be guaranteed atomic across threads using + * locks. By default, thread safety is disabled on sockets. + */ + bool __threadsafe_instructions; + + /** + * Lock which is acquired when an instruction is being written, and + * released when the instruction is finished being written. + */ + pthread_mutex_t __instruction_write_lock; + }; /** @@ -205,6 +219,35 @@ guac_socket* guac_socket_alloc(); */ void guac_socket_free(guac_socket* socket); +/** + * Declares that the given socket must behave in a threadsafe way. Calling + * this function on a socket guarantees that the socket will send instructions + * atomically. Without automatic threadsafe sockets, multiple threads writing + * to the same socket must ensure that instructions will not potentially + * overlap. + * + * @param socket The guac_socket to declare as threadsafe. + */ +void guac_socket_require_threadsafe(guac_socket* socket); + +/** + * Marks the beginning of a Guacamole protocol instruction. If threadsafety + * is enabled on the socket, other instructions will be blocked from sending + * until this instruction is complete. + * + * @param socket The guac_socket beginning an instruction. + */ +void guac_socket_instruction_begin(guac_socket* socket); + +/** + * Marks the end of a Guacamole protocol instruction. If threadsafety + * is enabled on the socket, other instructions will be allowed to send. + * + * @param socket The guac_socket ending an instruction. + */ +void guac_socket_instruction_end(guac_socket* socket); + + /** * Allocates and initializes a new guac_socket object with the given open * file descriptor. diff --git a/src/libguac/socket.c b/src/libguac/socket.c index 7ee29503..63193bb2 100644 --- a/src/libguac/socket.c +++ b/src/libguac/socket.c @@ -36,13 +36,14 @@ * * ***** END LICENSE BLOCK ***** */ +#include +#include +#include +#include +#include #include #include #include -#include -#include -#include -#include #ifdef __MINGW32__ #include @@ -154,6 +155,11 @@ guac_socket* guac_socket_alloc() { socket->__instructionbuf_parse_start = 0; socket->__instructionbuf_elementc = 0; + + /* Default to unsafe threading */ + socket->__threadsafe_instructions = false; + pthread_mutex_init(&(socket->__instruction_write_lock), NULL); + /* No handlers yet */ socket->read_handler = NULL; socket->write_handler = NULL; @@ -164,6 +170,26 @@ guac_socket* guac_socket_alloc() { } +void guac_socket_require_threadsafe(guac_socket* socket) { + socket->__threadsafe_instructions = true; +} + +void guac_socket_instruction_begin(guac_socket* socket) { + + /* Lock writes if threadsafety enabled */ + if (socket->__threadsafe_instructions) + pthread_mutex_lock(&(socket->__instruction_write_lock)); + +} + +void guac_socket_instruction_end(guac_socket* socket) { + + /* Lock writes if threadsafety enabled */ + if (socket->__threadsafe_instructions) + pthread_mutex_unlock(&(socket->__instruction_write_lock)); + +} + void guac_socket_free(guac_socket* socket) { /* Call free handler if defined */ @@ -171,6 +197,7 @@ void guac_socket_free(guac_socket* socket) { socket->free_handler(socket); guac_socket_flush(socket); + pthread_mutex_destroy(&(socket->__instruction_write_lock)); free(socket->__instructionbuf); free(socket); }