Add optional locks to socket.

This commit is contained in:
Michael Jumper 2013-06-28 14:17:12 -07:00
parent 400920b3bb
commit 51c00755ac
2 changed files with 74 additions and 4 deletions

View File

@ -38,6 +38,8 @@
#ifndef _GUAC_SOCKET_H #ifndef _GUAC_SOCKET_H
#define _GUAC_SOCKET_H #define _GUAC_SOCKET_H
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <unistd.h> #include <unistd.h>
@ -187,6 +189,18 @@ struct guac_socket {
*/ */
char* __instructionbuf_elementv[64]; 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); 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 * Allocates and initializes a new guac_socket object with the given open
* file descriptor. * file descriptor.

View File

@ -36,13 +36,14 @@
* *
* ***** END LICENSE BLOCK ***** */ * ***** END LICENSE BLOCK ***** */
#include <fcntl.h>
#include <inttypes.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#ifdef __MINGW32__ #ifdef __MINGW32__
#include <winsock2.h> #include <winsock2.h>
@ -154,6 +155,11 @@ guac_socket* guac_socket_alloc() {
socket->__instructionbuf_parse_start = 0; socket->__instructionbuf_parse_start = 0;
socket->__instructionbuf_elementc = 0; socket->__instructionbuf_elementc = 0;
/* Default to unsafe threading */
socket->__threadsafe_instructions = false;
pthread_mutex_init(&(socket->__instruction_write_lock), NULL);
/* No handlers yet */ /* No handlers yet */
socket->read_handler = NULL; socket->read_handler = NULL;
socket->write_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) { void guac_socket_free(guac_socket* socket) {
/* Call free handler if defined */ /* Call free handler if defined */
@ -171,6 +197,7 @@ void guac_socket_free(guac_socket* socket) {
socket->free_handler(socket); socket->free_handler(socket);
guac_socket_flush(socket); guac_socket_flush(socket);
pthread_mutex_destroy(&(socket->__instruction_write_lock));
free(socket->__instructionbuf); free(socket->__instructionbuf);
free(socket); free(socket);
} }