diff --git a/examples/http_server.c b/examples/http_server.c index 9803d153b6f794158c9d646619e42dd3098b95a9..8ec42d0b34d7ccb24cddd5f5ccd62835f6d17df6 100644 --- a/examples/http_server.c +++ b/examples/http_server.c @@ -73,6 +73,9 @@ static pthread_cond_t global_cond = PTHREAD_COND_INITIALIZER; #ifdef USE_CAMERA +/* try to open capture interface. + * returns 0 on success + */ int try_open_camera(struct global_state* state) { state->cam = camera_open(); @@ -158,6 +161,9 @@ int client_send_frame(struct client* client, frame* fr) return result; } +/* get frame from camera and send to client + * returns zero on success + */ int try_get_frame(struct client* client) { int result=-1; @@ -276,12 +282,17 @@ void* serve_client(void *ctxt) return (void*) (intptr_t) close(client->connfd); } +/* signal the global condition variable + */ void signal_to_bg_task() { pthread_mutex_lock(&global_mutex); pthread_cond_broadcast(&global_cond); pthread_mutex_unlock(&global_mutex); } +/* set flags to signal shutdown and + * signal global condition variable + */ void server_quit(struct global_state* s) { printf("Quitting\n"); @@ -355,10 +366,11 @@ int try_accept(struct global_state* state, struct client* client) #ifdef INFO printf("accepted connection\n"); #endif - // serve clients in separate thread, for three reasons + // serve clients in separate thread, for four reasons // 1. Illustrate threading // 2. Not blocking the user interface while serving client // 3. Prepare for serving multiple clients concurrently + // 4. The AXIS software requires capture to be run outside the main thread if (pthread_create(&state->client_thread, 0, serve_client, client)) { printf("Error pthread_create()\n"); perror("creating"); @@ -423,8 +435,8 @@ static void join_bg_thread(pthread_t* bg_thread, const char* msg) static void client_init(struct client* client) { - client->cam=0; - client->connfd=0; + client->cam=NULL; + client->connfd=-1; } int serve_clients(struct global_state* state) { @@ -455,7 +467,7 @@ int serve_clients(struct global_state* state) if(ret <0) { perror("poll"); result = errno; - goto failed_poll; + server_quit(state); } else if (is_running(state)) { if(try_accept(state, client)) { perror("try_accept"); @@ -463,7 +475,6 @@ int serve_clients(struct global_state* state) server_quit(state); }; } -failed_poll: free(client); } failed_to_alloc_client: @@ -473,11 +484,11 @@ failed_to_alloc_client: void init_global_state(struct global_state* state) { -pthread_mutex_lock(&global_mutex); + pthread_mutex_lock(&global_mutex); state->running=0; state->quit=0; state->cam=NULL; -pthread_mutex_unlock(&global_mutex); + pthread_mutex_unlock(&global_mutex); } int is_running(struct global_state* state) @@ -509,6 +520,9 @@ int create_socket(struct global_state* state) return 0; } +/* bind state->listenfd to port and listen + * returns 0 on success + */ int bind_and_listen(struct global_state* state, int port) { struct sockaddr_in serv_addr; diff --git a/examples/motion_client.c b/examples/motion_client.c index c69c50c4b4f88bd59ce2e8de569d8041877fb2dc..fb84c6bea12eaee1cc20816fdc541ffbc82476bf 100644 --- a/examples/motion_client.c +++ b/examples/motion_client.c @@ -24,7 +24,7 @@ static void motion_get(); int running=1; -int motionfd=0; +int motionfd=-1; struct hostent *motion_server; struct sockaddr_in motion_serv_addr; diff --git a/examples/motion_server.c b/examples/motion_server.c index d67f84340f51c0cfce13b86b13bd7f5a66926016..76489529eea6f0f4c8f8d32badbf0d85ff6bfecd 100644 --- a/examples/motion_server.c +++ b/examples/motion_server.c @@ -104,10 +104,6 @@ struct client{ static int client_write_string(struct client* client); -/////////////// client stuff - -struct client; - pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t global_cond = PTHREAD_COND_INITIALIZER; @@ -486,20 +482,19 @@ int serve_clients(struct global_state* state) result = errno; }; } -failed_poll: - ; } +failed_poll: return result; } void init_global_state(struct global_state* state) { -pthread_mutex_lock(&global_mutex); + pthread_mutex_lock(&global_mutex); state->running=0; state->quit=0; state->t_local=0; state->t_remote=0; -pthread_mutex_unlock(&global_mutex); + pthread_mutex_unlock(&global_mutex); } int create_socket(struct global_state* state) diff --git a/socket_examples/README-socket.md b/socket_examples/README-socket.md new file mode 100644 index 0000000000000000000000000000000000000000..791d0e207a6147545864a51fa0223a41c9b5dada --- /dev/null +++ b/socket_examples/README-socket.md @@ -0,0 +1,29 @@ +# Introduction to socket programming in C + +This mimimal example consists of two pars of files for TCP and UDP: +a server which replies with a message to the first client that +connects and then exits, and a client which connects to a server and +outputs whatever it receives. (In the UDP case, the client sends a +datagram and the server echoes it back, as there is no connection +established, so the "client" must start by sending a datagram. +Note that the terms "client" and "server" in the UDP case have a +more vague meaning, where the "client" is the one that takes the initiative +by sending the first datagram.) + +See "Socket programming in C" for more details. + +To build and run the example, do `make run`. See the Makefile for details + +An alternative to using simple_tcp_client is to use the telnet program to +connect to the server. To do that, start `simple_tcp_server` in one terminal, +and then type `telnet localhost 5000` in another. + +Sometimes, `telnet` is a useful tool to look at or debug text-based protocols +over TCP. + + +Please note that TCP is connection-based, but not UDP. Thus, when using +UDP, there is nothing like `listen()` or `accept()`. Instead, you simply +send datagrams using `sendto()` or `sendmsg()`, and receive datagrams +with `recvfrom()` or `recvmsg()`. See the manual pages for further information. + diff --git a/socket_examples/simple_tcp_client.c b/socket_examples/simple_tcp_client.c new file mode 100644 index 0000000000000000000000000000000000000000..aaa1774262cb3b91cdcba7119109cd8333b9ebb1 --- /dev/null +++ b/socket_examples/simple_tcp_client.c @@ -0,0 +1,102 @@ +/* +A minimal example of a TCP client + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SERVER_PORT +#define SERVER_PORT 5000 +#endif + +void init(const char* server_name, int port); +static void socket_init(); +static void socket_close(); +static void make_request(); + + +int sockfd=-1; +struct hostent *server; +struct sockaddr_in serv_addr; + +void init(const char* server_name, int port) +{ + server = gethostbyname(server_name); + if (server == NULL) { + fprintf(stderr,"ERROR, server name not found\n"); + exit(1); + } else { + bzero((char *) &serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length); + serv_addr.sin_port = htons(port); + } +} + +int main(int argc, char *argv[]) +{ + const char* server_name; + int port; + + if(argc>=2) { + server_name = argv[2]; + } else { + server_name = "localhost"; + } + if(argc==3) { + port = atoi(argv[3]); + } else { + port = SERVER_PORT; + } + + printf("simple_tcp_client: connecting to server: %s, port %d\n",server_name, port); + + init(server_name, port); + make_request(); + + return 0; +} + +#define BUFSZ 100 +static void make_request() +{ + char msg[BUFSZ]; +#ifdef DEBUG + printf("simple_tcp_client: connecting\n"); +#endif + socket_init(); + if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { + perror("ERROR connecting"); + } else { + int res = read(sockfd, msg, BUFSZ-1); + if(res < 0) { + perror("ERROR reading from motion server"); + } else { + msg[res]='\0'; /* ensure msg is null terminated */ + printf("simple_tcp_client: response: %s\n",msg); + } + socket_close(); + } +} + +static void socket_init() +{ + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + perror("creating motion socket"); + } +} +static void socket_close() +{ + if (sockfd) { + if(close(sockfd)){ + perror("closing motion socket"); + } + } + sockfd=-1; +} diff --git a/socket_examples/simple_tcp_server.c b/socket_examples/simple_tcp_server.c new file mode 100644 index 0000000000000000000000000000000000000000..a4da0ab1998c4d0c03ede8c82b7686b06950b85f --- /dev/null +++ b/socket_examples/simple_tcp_server.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include + +static int bind_server_socket(int fd, int port); + +/* + * create a server socket bound to port + * and listening. + * + * return positive file descriptor + * or negative value on error + */ +int create_server_socket(int port) +{ + int fd = -1; + + if(port < 0 || port > 65535) { + errno = EINVAL; + return -1; + } + fd = socket(AF_INET,SOCK_STREAM,0); + if(fd < 0) return fd; + if(bind_server_socket(fd,port) < 0) return -1; + + if(listen(fd,10)) return -1; + + return fd; +} + +static int bind_server_socket(int fd, int port){ + + struct sockaddr_in addr; + int val = 1; + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) { + perror("setsockopt"); + return -1; + } + + /* see man page ip(7) */ + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if(bind(fd, (struct sockaddr*) &addr, sizeof(addr))) return -1; + +#ifdef INFO + printf("simple_tcp_server: bound fd %d to port %d\n",fd,port); +#endif + + return fd; +} + +/* + * Serve one client: send a message and close the socket + */ +static int do_serve(int fd) +{ + int clientfd; + const char* msg = "Hello, socket!\n" + "I am a text\n" + "BYE.\n"; + size_t len = strlen(msg); + + printf("simple_tcp_server: attempting accept on fd %d\n",fd); + if((clientfd = accept(fd, NULL, NULL)) < 0) return -1; +#ifdef INFO + printf("simple_tcp_server: writing msg (len=%lu) to clientfd (%d)\n",len,clientfd); +#endif + +#ifdef WRITE_LOOP + size_t written = 0; + do { + int res = write(clientfd, msg, len); + if (res < 0) { + perror("write to clientfd"); + goto error; + } +#ifdef INFO + printf("simple_tcp_server: write returned %d\n",res); +#endif + written += res; + } while (written < len); +#else + { + int res = write(clientfd, msg, len); + if (res < 0) { + perror("write to clientfd"); + goto error; + } + } +#endif + + error: + printf("simple_tcp_server: closing clientfd (%d)\n",clientfd); + return close(clientfd); +} + +int main() +{ + int fd = create_server_socket(5000); + + if(fd < 0){ + perror("create_server_socket"); + return 1; + } + + do_serve(fd); + + printf("simple_tcp_server: closing socket: %d\n", fd); + close(fd); + + return 0; +} diff --git a/socket_examples/simple_udp_client.c b/socket_examples/simple_udp_client.c new file mode 100644 index 0000000000000000000000000000000000000000..16459b2f03078805c4ab18dc81a59ecb38567995 --- /dev/null +++ b/socket_examples/simple_udp_client.c @@ -0,0 +1,140 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define BUFSZ 1024 +#define SERVER_PORT 5000 + +/* + * create a datagram socket + */ +int create_socket() +{ + int fd = -1; + + fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); + return fd; +} + +#undef USE_IP_NUMBER +static int init_remote(struct sockaddr_in* remote_end, socklen_t remote_size, const char* server_str, int port ) +{ + memset((char*) &remote_end, 0, remote_size); + remote_end->sin_family = AF_INET; + remote_end->sin_port = htons(port); +#ifdef USE_IP_NUMBER + if (inet_aton(server_str , &remote_end->sin_addr) == 0) + { + fprintf(stderr, "inet_aton() failed\n"); + return -1; + } +#else + { + struct hostent* server; + server = gethostbyname(server_str); + if (server == NULL) { + fprintf(stderr,"ERROR, server name not found\n"); + return -1; + } else { + bcopy((char *)server->h_addr, (char *)&remote_end->sin_addr.s_addr, server->h_length); + } + } + +#endif + + +} + +/* + * Send a datagram to server and wait for one reply + */ +static int send_and_receive(int fd, const char* server_str, int port) +{ + char buf[BUFSZ]; + struct sockaddr_in remote_end; + socklen_t remote_size = sizeof(remote_end); + + if(port < 0 || port > 65535) { + errno = EINVAL; + return -1; + } + + printf("simple_udp_client: sending to %s:%d (fd=%d)\n",server_str,port,fd); + + init_remote(&remote_end, remote_size, server_str, port); + + + const char* msg = "Hello, socket!\n" + "I am a text\n" + "BYE.\n"; + ssize_t len = strlen(msg); + len = sendto(fd, msg, len, 0, (struct sockaddr*) &remote_end, remote_size); + if(len == -1){ + perror("sendto"); + return len; + } + printf("simple_udp_client: waiting for datagram on fd %d\n",fd); + + len = recvfrom(fd, buf, BUFSZ-1, 0, (struct sockaddr*) &remote_end, &remote_size); + if(len == -1){ + perror("recvfrom"); + return len; + } + buf[len] = 0; /* add null termination to string*/ + + + printf("simple_udp_client: received packet from %s:%d, len=%zu\n%s\n", + inet_ntoa(remote_end.sin_addr), + ntohs(remote_end.sin_port), + len, + buf); + + return 0; +} + +int main(int argc, char *argv[]) +{ + const char* server_str; + int port; + int fd; + + if(argc>=2) { + server_str = argv[1]; + } else { +#ifdef USE_IP_NUMBER + server_str = "127.0.0.1"; +#else + server_str = "localhost"; +#endif + } + if(argc==3) { + port = atoi(argv[2]); + } else { + port = SERVER_PORT; + } + + // printf("simple_client: connecting to server: %s, port %d\n",server_str, port); + fd = create_socket(); + + if(fd < 0){ + perror("create_socket"); + return 1; + } + + + printf("calling send_and_receive for %s,%d\n",server_str,port); + send_and_receive(fd, server_str, port); + + printf("simple_udp_client: closing socket: %d\n", fd); + close(fd); + + return 0; +} + diff --git a/socket_examples/simple_udp_server.c b/socket_examples/simple_udp_server.c new file mode 100644 index 0000000000000000000000000000000000000000..be6b48ac161dd7a3da5e551176b10f1a1f303ce6 --- /dev/null +++ b/socket_examples/simple_udp_server.c @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFSZ 1024 +#define SERVER_PORT 5000 + +static int bind_socket(int fd, int port); + +/* + * create a server socket bound to port + * and listening. + * + * return positive file descriptor + * or negative value on error + */ +int create_socket(int port) +{ + int fd = -1; + + if(port < 0 || port > 65535) { + errno = EINVAL; + return -1; + } + fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); + if(fd < 0) return fd; + if(bind_socket(fd,port) < 0) return -1; + printf("simple_udp_server: bound socket to port %d\n",port); + return fd; +} + +static int bind_socket(int fd, int port){ + + struct sockaddr_in addr; + // if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) { + // perror("setsockopt"); + // return -1; + // } + + /* see man page ip(7) */ + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if(bind(fd, (struct sockaddr*) &addr, sizeof(addr))) return -1; + +#ifdef INFO + printf("simple_server: bound fd %d to port %d\n",fd,port); +#endif + + return fd; +} + +/* + * Wait for one datagram and echo it back + */ +static int do_serve(int fd) +{ + char buf[BUFSZ]; + struct sockaddr_in remote_end; + socklen_t remote_size = sizeof(remote_end); + + // const char* msg = "Hello, socket!\n" + // "I am a text\n" + // "BYE.\n"; + // size_t len = strlen(msg); + ssize_t len; + + printf("simple_server: waiting for datagram on fd %d\n",fd); + + len = recvfrom(fd, buf, BUFSZ-1, 0, (struct sockaddr*) &remote_end, &remote_size); + if(len == -1){ + perror("recvfrom"); + return len; + } + buf[len] = 0; /* add null termination to string*/ + + + printf("simple_server: received packet from %s:%d, len=%zu\n%s\n", + inet_ntoa(remote_end.sin_addr), + ntohs(remote_end.sin_port), + len, + buf); + + len = sendto(fd, buf, len, 0, (struct sockaddr*) &remote_end, remote_size); + if(len == -1){ + perror("sendto"); + return len; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + const char* server_name; + int port; + int fd; + + if(argc>=2) { + server_name = argv[2]; + } else { + server_name = "localhost"; + } + if(argc==3) { + port = atoi(argv[3]); + } else { + port = SERVER_PORT; + } + + // printf("simple_client: connecting to server: %s, port %d\n",server_name, port); + fd = create_socket(port); + + if(fd < 0){ + perror("create_socket"); + return 1; + } + + do_serve(fd); + + printf("simple_server: closing socket: %d\n", fd); + close(fd); + + return 0; +} + diff --git a/thread_examples/Makefile b/thread_examples/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..05a0e6469e35700b609134fb03815e4cf6c0d375 --- /dev/null +++ b/thread_examples/Makefile @@ -0,0 +1,16 @@ +CC=gcc + +PRGS=thread_example thread_example_monitor thread_example_semaphore +.PHONY: all clean distclean +all: $(PRGS) + +# CPPFLAGS+=-DDEBUG +CFLAGS+=-g -Wall -Werror -pedantic -pedantic-errors +LDLIBS=-lpthread + +%: %.c + +clean: + +distclean: clean + -rm $(PRGS) diff --git a/thread_examples/README-pthread.md b/thread_examples/README-pthread.md new file mode 100644 index 0000000000000000000000000000000000000000..4e093a6a21bddf93ac93ec9d91b4bd64ec98aa14 --- /dev/null +++ b/thread_examples/README-pthread.md @@ -0,0 +1,122 @@ +# Introduction to pthreads + +These three examples are aimed at showing +* how to create threads in C using the pthread library +* how to use monitors +* how to use semaphores + +Please note that when building programs using the pthread library +it must be linked with `libpthread` by providing the flag `-lpthread`. +See the Makefile for details. + +# Creating threads + +The `pthread_create()` function, +```C +int pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg); +``` +starts a new thread in the +calling process. The new thread starts execution by +invoking start_routine(); arg is passed as the sole argument +of start_routine(). + +The sytax of function pointers in C can be somewhat confusing, but the type +```C +void * (*f)(void *) +``` +declares a pointer, f, which can point to a function with the signature +```C +void* function(void *p); +``` +That is, a function with one `void *` parameter, and return type `void *`. + +Before returning, a successful call to `pthread_create()` +stores the ID of the new thread in the buffer pointed to by +thread; this identifier is used to refer to the thread in +subsequent calls to other pthreads functions. + +The new thread terminates in one of the following ways: + + * It calls `pthread_exit(3),` specifying an exit status value + that is available to another thread in the same process + that calls `pthread_join(3).` + + * It returns from `start_routine().` This is equivalent to + calling `pthread_exit(3)` with the value supplied in the + return statement. + + * It is canceled (see `pthread_cancel(3)).` + + * Any of the threads in the process calls `exit(3),` or the + main thread performs a return from `main().` This causes + the termination of all threads in the process. + +## Relevant manual pages: + + pthreads(7) + pthread_create(3) + pthread_attr_init(3) + pthread_join(3) + + +## Minimal example + +The file thread_example.c contains a minimal example, showing +how to create a thread, how to pass arguments to the thread function +and how to use `pthread_join()` to wait for a thread to finish. +The code shows two alternatives, one ignoring the return value +by passing `NULL` to `pthread_join(),` and one to get the +value (including the type casting needed to pass an int in +a void*. + +# Monitors + +The file thread_example_monitor.c shows how to implement +rendez-vous using a monitor (i.e., a pthread mutex and a condition variable). + +Note that in pthreads, the condition variable is a separate entity, +and the tie to a certain mutex is done by passing pointers to +both the mutex and the condition variable to pthread_cond_wait(). +This allows having more than one condition variable, to make +signalling more fine-grained than notifyAll() in Java. + +When allocated on the stack, the mutex and condition can be given +default values using initializer macros: +```C +pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t cnd = PTHREAD_COND_INITIALIZER; +``` +Another way to initialise a mutex is to use the function +`pthread_mutex_init().` See the man page for details. + +By default, a pthread mutex is not recursive, meaning that if a thread +attemtps to lock a mutex it already holds, it will deadlock. To get the +same semantics as java (i.e., a thread can lock the same mutex multiple +times) the mutex must be created as recursive. See the manual page for +details. + +## Relevant manual pages: + + pthread_mutex_init(3) + pthread_mutex_destroy(3) + pthread_mutex_lock(3) + pthread_mutex_trylock(3) + pthread_mutex_unlock(3) + pthread_mutexattr_init(3) + pthread_mutexattr_destroy(3) + + +# Semaphores + +The file thread_example_semaphore.c shows an example +using a mutex for mutual exclusion and a semaphore for signalling. +It also shows how to initialize a mutex that is a struct member, +using `pthread_mutex_init()`. + +## Relevant manual pages: + +sem_init(3) +sem_destroy(3) +sem_post(3) +sem_wait(3) \ No newline at end of file diff --git a/thread_examples/thread_example.c b/thread_examples/thread_example.c new file mode 100644 index 0000000000000000000000000000000000000000..b9201757ee38837bd7027ebb80d1e27ca04b910e --- /dev/null +++ b/thread_examples/thread_example.c @@ -0,0 +1,71 @@ + +#include +#include +#include + + +#include +#include + + +pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t cnd = PTHREAD_COND_INITIALIZER; + +struct global_data{ + const int count; +}; + +void * task_a(void * ctx) +{ + struct global_data *d = ctx; + int i; + + printf("started task_a\n"); + for(i=0; i < d->count; ++i){ + printf("task_a: %d\n",i); + } + return (void*) 17; +} + +void * task_b(void * ctx) +{ + struct global_data *d = ctx; + int i; + + printf("started task_b\n"); + for(i=0; i < d->count; ++i){ + printf("task_b: %d\n",i); + } + return (void*) 42; +} + + +int main() +{ + struct global_data data = {10}; + pthread_t thread_a; + pthread_t thread_b; +#ifdef RETURN_INT + void *ret_a = 0; + void *ret_b = 0; +#endif + + + if(pthread_create(&thread_a, NULL, task_a, &data)){ + printf("Failed to create thread_a\n"); + exit(1); + } + if(pthread_create(&thread_b, NULL, task_b, &data)){ + printf("Failed to create thread_b\n"); + exit(2); + } +#ifdef RETURN_INT + pthread_join(thread_a, &ret_a); + pthread_join(thread_b, &ret_b); + printf("thread_a returned: %d\nthread_b returned: %d\n", (int)(long)ret_a, (int)(long)ret_b); +#else + pthread_join(thread_a, NULL); + pthread_join(thread_b, NULL); +#endif + return 0; +} diff --git a/thread_examples/thread_example_monitor.c b/thread_examples/thread_example_monitor.c new file mode 100644 index 0000000000000000000000000000000000000000..0b244b5ee8fd4126d27acc6886e139b823bf5073 --- /dev/null +++ b/thread_examples/thread_example_monitor.c @@ -0,0 +1,80 @@ + +#include +#include +#include + + +#include +#include + + +pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t cnd = PTHREAD_COND_INITIALIZER; + +struct global_data{ + char task; + const int count; +}; + +void * task_a(void * ctx) +{ + struct global_data *d = ctx; + int i; + + printf("started task_a\n"); + for(i=0; i < d->count; ++i){ + pthread_mutex_lock(&mtx); + while(d->task != 'a') pthread_cond_wait(&cnd, &mtx); + printf("task_a: %d\n",i); + d->task = 'b'; + pthread_cond_signal(&cnd); + pthread_mutex_unlock(&mtx); + } + return NULL; +} + +void * task_b(void * ctx) +{ + struct global_data *d = ctx; + int i; + + printf("started task_b\n"); + for(i=0; i < d->count; ++i){ + pthread_mutex_lock(&mtx); + while(d->task != 'b') pthread_cond_wait(&cnd, &mtx); + printf("task_b: %d\n",i); + d->task = 'a'; + pthread_cond_signal(&cnd); + pthread_mutex_unlock(&mtx); + } + return NULL; +} + + +int main() +{ + struct global_data data = {0,10}; + pthread_t thread_a; + + pthread_t thread_b; + + + + if(pthread_create(&thread_a, NULL, task_a, &data)){ + printf("Failed to create thread_a\n"); + exit(1); + } + if(pthread_create(&thread_b, NULL, task_b, &data)){ + printf("Failed to create thread_b\n"); + exit(2); + } + sleep(1); + pthread_mutex_lock(&mtx); + printf("setting task to a\n"); + data.task = 'a'; + pthread_cond_signal(&cnd); + pthread_mutex_unlock(&mtx); + pthread_join(thread_a, NULL); + pthread_join(thread_b, NULL); + return 0; +} diff --git a/thread_examples/thread_example_semaphore.c b/thread_examples/thread_example_semaphore.c new file mode 100644 index 0000000000000000000000000000000000000000..402edf02ee00fbea57f12d7246fd4ddb5a6f99e8 --- /dev/null +++ b/thread_examples/thread_example_semaphore.c @@ -0,0 +1,95 @@ +#include +#include +#include + + +#include +#include + +#include + + + +struct global_data{ + int count; + pthread_mutex_t mtx; + sem_t sem_a; + sem_t sem_b; +}; + +void * task_a(void * ctx) +{ + struct global_data *d = ctx; + int i; + + printf("started task_a\n"); + for(i=0; i < d->count; ++i){ + sem_wait(&d->sem_a); + pthread_mutex_lock(&d->mtx); + printf("task_a: %d\n",i); + sem_post(&d->sem_b); + pthread_mutex_unlock(&d->mtx); + } + return NULL; +} + +void * task_b(void * ctx) +{ + struct global_data *d = ctx; + int i; + + printf("started task_b\n"); + for(i=0; i < d->count; ++i){ + sem_wait(&d->sem_b); + pthread_mutex_lock(&d->mtx); + printf("task_b: %d\n",i); + sem_post(&d->sem_a); + pthread_mutex_unlock(&d->mtx); + } + return NULL; +} + +int global_data_init(struct global_data *d) +{ + d->count = 10; + pthread_mutex_init(&d->mtx, NULL); + if(sem_init(&d->sem_a, 0, 0)){ + return 1; + } + if(sem_init(&d->sem_b, 0, 0)){ + return 1; + } + return 0; +} + +int main() +{ + struct global_data data; + pthread_t thread_a; + pthread_t thread_b; + int res; + + if(global_data_init(&data)){ + perror("global_data_init"); + exit(1); + } + + if((res=pthread_create(&thread_a, NULL, task_a, &data))){ + errno = res; + perror("Failed to create thread_a\n"); + exit(2); + } + if((res=pthread_create(&thread_b, NULL, task_b, &data))){ + errno = res; + perror("Failed to create thread_a\n"); + exit(3); + } + sleep(1); + pthread_mutex_lock(&data.mtx); + printf("signalling semaphore\n"); + sem_post(&data.sem_a); + pthread_mutex_unlock(&data.mtx); + pthread_join(thread_a, NULL); + pthread_join(thread_b, NULL); + return 0; +}