From: <ljs...@us...> - 2012-06-10 18:29:47
|
Revision: 814 http://cadcdev.svn.sourceforge.net/cadcdev/?rev=814&view=rev Author: ljsebald Date: 2012-06-10 18:29:40 +0000 (Sun, 10 Jun 2012) Log Message: ----------- Update reader/writer semaphores a bit (adding a few new functions and such). Modified Paths: -------------- kos/examples/dreamcast/basic/threading/rwsem/rwsem_test.c kos/include/kos/rwsem.h kos/kernel/net/net_tcp.c kos/kernel/thread/rwsem.c kos/kernel/thread/thread.c Modified: kos/examples/dreamcast/basic/threading/rwsem/rwsem_test.c =================================================================== --- kos/examples/dreamcast/basic/threading/rwsem/rwsem_test.c 2012-06-10 18:28:57 UTC (rev 813) +++ kos/examples/dreamcast/basic/threading/rwsem/rwsem_test.c 2012-06-10 18:29:40 UTC (rev 814) @@ -22,21 +22,22 @@ #define UNUSED __attribute__((unused)) -rw_semaphore_t *s = NULL; +rw_semaphore_t s = RWSEM_INITIALIZER; uint32 number = 0; void *writer0(void *param UNUSED) { int i; for(i = 0; i < 20; ++i) { - if(rwsem_write_lock(s)) { + if(rwsem_write_lock(&s)) { printf("Writer 0 could not obtain write lock!\n"); + perror("rwsem_write_lock"); return NULL; } printf("Writer 0 obtained write lock\n"); number += 8; - rwsem_write_unlock(s); + rwsem_write_unlock(&s); thd_sleep(10); } @@ -49,14 +50,15 @@ int i; for(i = 0; i < 17; ++i) { - if(rwsem_write_lock(s)) { + if(rwsem_write_lock(&s)) { printf("Writer 1 could not obtain write lock!\n"); + perror("rwsem_write_lock"); return NULL; } printf("Writer 1 obtained write lock\n"); number *= 3; - rwsem_write_unlock(s); + rwsem_write_unlock(&s); thd_sleep(5); } @@ -69,14 +71,15 @@ int i; for(i = 0; i < 12; ++i) { - if(rwsem_read_lock(s)) { + if(rwsem_read_lock(&s)) { printf("Reader 0 could not obtain read lock!\n"); + perror("rwsem_read_lock"); return NULL; } printf("Reader 0 obtained read lock\n"); printf("Number: %lu\n", number); - rwsem_read_unlock(s); + rwsem_read_unlock(&s); thd_sleep(20); } @@ -89,14 +92,15 @@ int i; for(i = 0; i < 23; ++i) { - if(rwsem_read_lock(s)) { + if(rwsem_read_lock(&s)) { printf("Reader 1 could not obtain read lock!\n"); + perror("rwsem_read_lock"); return NULL; } printf("Reader 1 obtained read lock\n"); printf("Number * 2: %lu\n", number * 2); - rwsem_read_unlock(s); + rwsem_read_unlock(&s); thd_sleep(16); } @@ -115,14 +119,6 @@ printf("KallistiOS Reader/Writer Semaphore test program\n"); - /* Create the reader/writer semaphore that will be used. */ - s = rwsem_create(); - - if(!s) { - printf("Could not create RW semaphore, bailing out!\n"); - arch_exit(); - } - printf("About to create threads\n"); w0 = thd_create(0, writer0, NULL); w1 = thd_create(0, writer1, NULL); @@ -135,15 +131,16 @@ thd_join(r0, NULL); thd_join(r1, NULL); - if(rwsem_read_lock(s)) { + if(rwsem_read_lock(&s)) { printf("Could not obtain final read lock!\n"); + perror("rwsem_read_lock"); arch_exit(); } printf("Final number: %lu\n", number); - rwsem_read_unlock(s); - rwsem_destroy(s); + rwsem_read_unlock(&s); + rwsem_destroy(&s); printf("Reader/Writer semaphore tests completed successfully!\n"); return 0; Modified: kos/include/kos/rwsem.h =================================================================== --- kos/include/kos/rwsem.h 2012-06-10 18:28:57 UTC (rev 813) +++ kos/include/kos/rwsem.h 2012-06-10 18:29:40 UTC (rev 814) @@ -1,7 +1,7 @@ /* KallistiOS ##version## include/kos/rwsem.h - Copyright (C) 2008, 2010 Lawrence Sebald + Copyright (C) 2008, 2010, 2012 Lawrence Sebald */ @@ -29,7 +29,8 @@ __BEGIN_DECLS -#include <sys/queue.h> +#include <stddef.h> +#include <kos/thread.h> /** \brief Reader/writer semaphore structure. @@ -39,42 +40,80 @@ \headerfile kos/rwsem.h */ typedef struct rw_semaphore { - /** \cond */ - /* Entry into the global list of r/w semaphores. */ - LIST_ENTRY(rw_semaphore) list; - /** \endcond */ + /** \brief Are we initialized? */ + int initialized; /** \brief The number of readers that are currently holding the lock. */ int read_count; - /* \brief The state of the write lock. */ - int write_lock; + /** \brief The thread holding the write lock. */ + kthread_t *write_lock; + + /** \brief Space for one reader who's trying to upgrade to a writer. */ + kthread_t *reader_waiting; } rw_semaphore_t; -/** \cond */ -LIST_HEAD(rwsemlist, rw_semaphore); -/** \endcond */ +/** \brief Initializer for a transient reader/writer semaphore */ +#define RWSEM_INITIALIZER { 1, 0, NULL, NULL } /** \brief Allocate a reader/writer semaphore. This function allocates a new reader/writer lock that is initially not locked either for reading or writing. - \return The created semaphore, or NULL on failure (errno will be set to - ENOMEM to indicate that the system is out of memory). + This function is formally deprecated, and should not be used in newly + written code. Instead, please use rwsem_init(). + + \return The created semaphore, or NULL on failure (errno will be set as + appropriate). + + \par Error Conditions: + \em ENOMEM - out of memory */ -rw_semaphore_t *rwsem_create(); +rw_semaphore_t *rwsem_create() __attribute__((deprecated)); +/** \brief Initialize a reader/writer semaphore. + + This function initializes a new reader/writer semaphore for use. + + \retval 0 On success (no error conditions currently defined). +*/ +int rwsem_init(rw_semaphore_t *s); + /** \brief Destroy a reader/writer semaphore. This function cleans up a reader/writer semaphore. It is an error to attempt to destroy a r/w semaphore that is locked either for reading or writing. - \param s The r/w semaphore to destroy. It must be completely - unlocked. + \param s The r/w semaphore to destroy. + \retval 0 On success. + \retval -1 On error, errno will be set as appropriate. + + \par Error Conditions: + \em EBUSY - the semaphore is still locked */ -void rwsem_destroy(rw_semaphore_t *s); +int rwsem_destroy(rw_semaphore_t *s); +/** \brief Lock a reader/writer semaphore for reading (with a timeout). + + This function attempts to lock the r/w semaphore for reading. If the + semaphore is locked for writing, this function will block until it is + possible to obtain the lock for reading or the timeout expires. This + function is <b>NOT</b> safe to call inside of an interrupt. + + \param s The r/w semaphore to lock. + \param timeout The maximum time to wait (in milliseconds). + \retval 0 On success + \retval -1 On error, errno will be set as appropriate. + + \par Error Conditions: + \em EPERM - called inside an interrupt \n + \em ETIMEDOUT - the timeout expires before the lock can be acquired \n + \em EINVAL - the timeout value is invalid \n + \em EINVAL - the semaphore is not initialized +*/ +int rwsem_read_lock_timed(rw_semaphore_t *s, int timeout); + /** \brief Lock a reader/writer semaphore for reading. This function attempts to lock the r/w semaphore for reading. If the @@ -83,14 +122,35 @@ call inside of an interrupt. \param s The r/w semaphore to lock. - \retval -1 On error, errno will be set to EPERM if this function is - called inside an interrupt or EINTR if it is interrupted. - \retval 0 On success. - \sa rwsem_write_lock - \sa rwsem_read_trylock + \retval 0 On success + \retval -1 On error, errno will be set as appropriate. + + \par Error Conditions: + \em EPERM - called inside an interrupt \n + \em EINVAL - the semaphore is not initialized */ int rwsem_read_lock(rw_semaphore_t *s); +/** \brief Lock a reader/writer semaphore for writing (with a timeout). + + This function attempts to lock the r/w semaphore for writing. If the + semaphore is locked for reading or writing, this function will block until + it is possible to obtain the lock for writing or the timeout expires. This + function is <b>NOT</b> safe to call inside of an interrupt. + + \param s The r/w semaphore to lock. + \param timeout The maximum time to wait (in milliseconds). + \retval 0 On success. + \retval -1 On error, errno will be set as appropriate. + + \par Error Conditions: + \em EPERM - called inside an interrupt \n + \em ETIMEDOUT - the timeout expires before the lock can be acquired \n + \em EINVAL - the timeout value is invalid \n + \em EINVAL - the semaphore is not initialized +*/ +int rwsem_write_lock_timed(rw_semaphore_t *s, int timeout); + /** \brief Lock a reader/writer semaphore for writing. This function attempts to lock the r/w semaphore for writing. If the @@ -99,11 +159,12 @@ safe to call inside of an interrupt. \param s The r/w semaphore to lock. - \retval -1 On error, errno will be set to EPERM if this function is - called inside an interrupt or EINTR if it is interrupted. \retval 0 On success. - \sa rwsem_read_lock - \sa rwsem_write_trylock + \retval -1 On error, errno will be set as appropriate. + + \par Error conditions: + \em EPERM - called inside an interrupt \n + \em EINVAL - the semaphore is not initialized */ int rwsem_write_lock(rw_semaphore_t *s); @@ -112,8 +173,12 @@ This function releases one instance of the read lock on the r/w semaphore. \param s The r/w semaphore to release the read lock on. - \retval 0 On success (no error conditions defined). - \sa rwsem_write_unlock + \retval 0 On success. + \retval -1 On error, errno will be set as appropriate. + + \par Error Conditions: + \em EPERM - the read lock is not currently held \n + \em EINVAL - the semaphore is not initialized */ int rwsem_read_unlock(rw_semaphore_t *s); @@ -122,11 +187,35 @@ This function releases one instance of the write lock on the r/w semaphore. \param s The r/w semaphore to release the write lock on. - \retval 0 On success (no error conditions defined). - \sa rwsem_read_unlock + \retval 0 On success. + \retval -1 On error, errno will be set as appropriate. + + \par Error Conditions: + \em EPERM - the write lock is not currently held by the calling + thread \n + \em EINVAL - the semaphore is not initialized */ int rwsem_write_unlock(rw_semaphore_t *s); +/** \brief Unlock a reader/writer semaphore. + + This function releases the lock held by the current thread on the specified + reader/writer semaphore. This function will automatically determine which + lock is held by the calling thread and release it as appropriate. + + This function is <b>NOT</b> safe to call (in general) if you do not hold the + lock! + + \param s The r/w semaphore to release the lock on. + \retval 0 On success. + \retval -1 On error, errno will be set as appropriate. + + \par Error Conditions: + \em EPERM - the lock is not currently held by the calling thread \n + \em EINVAL - the semaphore is not initialized +*/ +int rwsem_unlock(rw_semaphore_t *s); + /** \brief Attempt to lock a reader/writer semaphore for reading. This function attempts to lock the r/w semaphore for reading. If for any @@ -134,11 +223,12 @@ error. This function is safe to call inside an interrupt. \param s The r/w semaphore to attempt to lock. - \retval -1 On error, errno will be set to EWOULDBLOCK if a call to - rwsem_read_lock would normally block. \retval 0 On success. - \sa rwsem_write_trylock - \sa rwsem_read_lock + \retval -1 On error, errno will be set as appropriate. + + \par Error Conditions: + \em EWOULDBLOCK - a call to rwsem_read_lock would block \n + \em EINVAL - the semaphore is not initialized */ int rwsem_read_trylock(rw_semaphore_t *s); @@ -149,14 +239,44 @@ error. This function is safe to call inside an interrupt. \param s The r/w semaphore to attempt to lock. - \retval -1 On error, errno will be set to EWOULDBLOCK if a call to - rwsem_write_lock would normally block. \retval 0 On success. - \sa rwsem_read_trylock - \sa rwsem_write_lock + \retval -1 On error, errno will be set as appropriate. + + \par Error Conditions: + \em EWOULDBLOCK - a call to rwsem_write_lock would block \n + \em EINVAL - the semaphore is not initialized */ int rwsem_write_trylock(rw_semaphore_t *s); +/** \brief Upgrade a thread from reader status to writer status (with a + timeout). + + This function will upgrade the lock on the calling thread from a reader + state to a writer state. If it cannot do this at the moment, it will block + until it is possible. This function is <b>NOT</b> safe to call inside an + interrupt. + + You can only have one reader waiting to upgrade at a time, otherwise the + state would potentially become corrupted between when this is called and + when you get the lock. If you get -1 back from this, you must not assume + that you can write safely! On error, the calling thread will still hold a + read lock. + + \param s The r/w semaphore to upgrade. + \param timeout The maximum time to wait (in milliseconds). + \retval 0 On success. + \retval -1 On error, errno will be set as appropriate. + + \par Error Conditions: + \em EPERM - called inside an interrupt \n + \em EINVAL - the semaphore is not initialized \n + \em EINVAL - the timeout value is invalid \n + \em EBUSY - another reader has already requested an upgrade \n + \em ETIMEDOUT - the timeout expired before the write lock could be + acquired +*/ +int rwsem_read_upgrade_timed(rw_semaphore_t *s, int timeout); + /** \brief Upgrade a thread from reader status to writer status. This function will upgrade the lock on the calling thread from a reader @@ -164,11 +284,20 @@ until it is possible. This function is <b>NOT</b> safe to call inside an interrupt. + You can only have one reader waiting to upgrade at a time, otherwise the + state would potentially become corrupted between when this is called and + when you get the lock. If you get -1 back from this, you must not assume + that you can write safely! On error, the calling thread will still hold a + read lock. + \param s The r/w semaphore to upgrade. - \retval -1 On error, errno will be set to EPERM if called inside an - interrupt or EINTR if interrupted. \retval 0 On success. - \sa rwsem_read_tryupgrade + \retval -1 On error, errno will be set as appropriate. + + \par Error Conditions: + \em EPERM - called inside an interrupt \n + \em EINVAL - the semaphore is not initialized \n + \em EBUSY - another reader has already requested an upgrade */ int rwsem_read_upgrade(rw_semaphore_t *s); @@ -180,10 +309,13 @@ interrupt. Note that on error, the read lock is still held! \param s The r/w semaphore to upgrade. - \retval -1 On error, errno will be set to EWOULDBLOCK if a call to - rwsem_read_upgrade would normally block. \retval 0 On success. - \sa rwsem_read_upgrade + \retval -1 On error, errno will be set as appropriate. + + \par Error Conditions: + \em EWOULDBLOCK - a call to rwsem_read_upgrade would block \n + \em EBUSY - another reader has already requested an upgrade \n + \em EINVAL - the sempahore is not initialized */ int rwsem_read_tryupgrade(rw_semaphore_t *s); @@ -208,12 +340,6 @@ */ int rwsem_write_locked(rw_semaphore_t *s); -/* Init / shutdown */ -/** \cond */ -int rwsem_init(); -void rwsem_shutdown(); -/** \endcond */ - __END_DECLS #endif /* __KOS_RWSEM_H */ Modified: kos/kernel/net/net_tcp.c =================================================================== --- kos/kernel/net/net_tcp.c 2012-06-10 18:28:57 UTC (rev 813) +++ kos/kernel/net/net_tcp.c 2012-06-10 18:29:40 UTC (rev 814) @@ -178,7 +178,7 @@ LIST_HEAD(tcp_sock_list, tcp_sock); static struct tcp_sock_list tcp_socks = LIST_HEAD_INITIALIZER(0); -static rw_semaphore_t *tcp_sem = NULL; +static rw_semaphore_t tcp_sem = RWSEM_INITIALIZER; static int thd_cb_id = 0; /* Default starting window size for connections. This should be big enough as a @@ -276,20 +276,20 @@ sock->sndbuf_sz = TCP_DEFAULT_WINDOW; if(irq_inside_int()) { - if(rwsem_write_trylock(tcp_sem)) { + if(rwsem_write_trylock(&tcp_sem)) { free(sock); errno = EWOULDBLOCK; return -1; } } else { - rwsem_write_lock(tcp_sem); + rwsem_write_lock(&tcp_sem); } hnd->data = sock; LIST_INSERT_HEAD(&tcp_socks, sock, sock_list); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); return 0; } @@ -302,17 +302,17 @@ retry: if(irq_inside_int()) { - if(rwsem_write_trylock(tcp_sem)) { + if(rwsem_write_trylock(&tcp_sem)) { errno = EWOULDBLOCK; return; } } else { - rwsem_write_lock(tcp_sem); + rwsem_write_lock(&tcp_sem); } if(!(sock = (struct tcp_sock *)hnd->data)) { - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); errno = EBADF; return; } @@ -320,7 +320,7 @@ if(irq_inside_int()) { if(mutex_trylock(&sock->mutex)) { errno = EWOULDBLOCK; - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); return; } } @@ -333,7 +333,7 @@ happening if you're sane... */ if(sock->state == (TCP_STATE_LISTEN | TCP_STATE_ACCEPTING)) { mutex_unlock(&sock->mutex); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); if(irq_inside_int()) { errno = EWOULDBLOCK; @@ -427,11 +427,10 @@ mutex_destroy(&sock->mutex); free(sock); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); return; ret_no_remove: - if(sock->state != TCP_STATE_LISTEN) sock->intflags = TCP_IFLAG_CANBEDEL; @@ -444,7 +443,7 @@ /* Don't free anything here, it will be dealt with later on in the net_thd callback. */ mutex_unlock(&sock->mutex); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); return; } @@ -462,20 +461,20 @@ } if(irq_inside_int()) { - if(rwsem_read_trylock(tcp_sem)) { + if(rwsem_read_trylock(&tcp_sem)) { errno = EWOULDBLOCK; return -1; } } else { - rwsem_read_lock(tcp_sem); + rwsem_read_lock(&tcp_sem); } /* Lock the mutex on the socket itself first. We need to pull some data from it that doesn't affect the rest of the list, so let's start there... */ if(!(sock = (struct tcp_sock *)hnd->data)) { errno = EBADF; - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); return -1; } @@ -484,7 +483,7 @@ if(mutex_trylock(&sock->mutex)) { errno = EWOULDBLOCK; - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); return -1; } } @@ -493,7 +492,7 @@ canblock = !(sock->flags & FS_SOCKET_NONBLOCK); } - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); /* Make sure the socket is listening... */ if(sock->state != TCP_STATE_LISTEN) { @@ -520,7 +519,7 @@ graceful fashion. */ if(sock->state == TCP_STATE_CLOSED) { mutex_unlock(&sock->mutex); - rwsem_write_lock(tcp_sem); + rwsem_write_lock(&tcp_sem); mutex_lock(&sock->mutex); free(sock->listen.queue); cond_destroy(&sock->listen.cv); @@ -529,7 +528,7 @@ mutex_destroy(&sock->mutex); free(sock); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); errno = EINTR; /* Close enough, I suppose. */ return -1; @@ -659,7 +658,7 @@ } if(irq_inside_int()) { - if(rwsem_write_trylock(tcp_sem)) { + if(rwsem_write_trylock(&tcp_sem)) { /* Kabuki dance to clean things up... */ mutex_unlock(&sock->mutex); @@ -704,7 +703,7 @@ else { sock->state |= TCP_STATE_ACCEPTING; mutex_unlock(&sock->mutex); - rwsem_write_lock(tcp_sem); + rwsem_write_lock(&tcp_sem); mutex_lock(&sock->mutex); } @@ -733,7 +732,7 @@ sock->state &= ~TCP_STATE_ACCEPTING; mutex_unlock(&sock->mutex); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); return fd; } @@ -791,23 +790,24 @@ } if(irq_inside_int()) { - if(rwsem_write_trylock(tcp_sem)) { + if(rwsem_write_trylock(&tcp_sem)) { errno = EWOULDBLOCK; return -1; } } else { - rwsem_write_lock(tcp_sem); + rwsem_write_lock(&tcp_sem); } if(!(sock = (struct tcp_sock *)hnd->data)) { - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); errno = EBADF; return -1; } if(irq_inside_int()) { if(mutex_trylock(&sock->mutex)) { + rwsem_write_unlock(&tcp_sem); errno = EWOULDBLOCK; return -1; } @@ -820,19 +820,19 @@ bound. */ if(sock->state == TCP_STATE_LISTEN) { mutex_unlock(&sock->mutex); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); errno = EINVAL; return -1; } else if(sock->state != TCP_STATE_CLOSED) { mutex_unlock(&sock->mutex); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); errno = EISCONN; return -1; } else if(sock->local_addr.sin6_port) { mutex_unlock(&sock->mutex); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); errno = EINVAL; return -1; } @@ -841,7 +841,7 @@ on the socket itself */ if(addr->sa_family != sock->domain) { mutex_unlock(&sock->mutex); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); errno = EINVAL; return -1; } @@ -857,7 +857,7 @@ if(irq_inside_int()) { if(mutex_trylock(&iter->mutex)) { mutex_unlock(&sock->mutex); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); errno = EWOULDBLOCK; return -1; } @@ -869,7 +869,7 @@ if(iter->local_addr.sin6_port == realaddr6.sin6_port) { mutex_unlock(&iter->mutex); mutex_unlock(&sock->mutex); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); errno = EADDRINUSE; return -1; } @@ -893,7 +893,7 @@ if(irq_inside_int()) { if(mutex_trylock(&iter->mutex)) { mutex_unlock(&sock->mutex); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); errno = EWOULDBLOCK; return -1; } @@ -917,7 +917,7 @@ /* Release the locks, we're done */ mutex_unlock(&sock->mutex); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); return 0; } @@ -978,23 +978,24 @@ } if(irq_inside_int()) { - if(rwsem_write_trylock(tcp_sem)) { + if(rwsem_write_trylock(&tcp_sem)) { errno = EWOULDBLOCK; return -1; } } else { - rwsem_write_lock(tcp_sem); + rwsem_write_lock(&tcp_sem); } if(!(sock = (struct tcp_sock *)hnd->data)) { - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); errno = EBADF; return -1; } if(irq_inside_int()) { if(mutex_trylock(&sock->mutex)) { + rwsem_write_unlock(&tcp_sem); errno = EWOULDBLOCK; return -1; } @@ -1016,7 +1017,7 @@ } mutex_unlock(&sock->mutex); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); return -1; } @@ -1024,7 +1025,7 @@ on the socket itself */ if(addr->sa_family != sock->domain) { mutex_unlock(&sock->mutex); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); errno = EINVAL; return -1; } @@ -1033,7 +1034,7 @@ if(IN6_IS_ADDR_UNSPECIFIED(&realaddr6.sin6_addr) || realaddr6.sin6_port == 0) { mutex_unlock(&sock->mutex); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); errno = EADDRNOTAVAIL; return -1; } @@ -1053,7 +1054,7 @@ if(irq_inside_int()) { if(mutex_trylock(&iter->mutex)) { mutex_unlock(&sock->mutex); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); errno = EWOULDBLOCK; return -1; } @@ -1087,14 +1088,14 @@ if(!(sock->data.rcvbuf = (uint8_t *)malloc(sock->rcvbuf_sz))) { errno = ENOBUFS; mutex_unlock(&sock->mutex); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); return -1; } if(!(sock->data.sndbuf = (uint8_t *)malloc(sock->sndbuf_sz))) { errno = ENOBUFS; mutex_unlock(&sock->mutex); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); free(sock->data.rcvbuf); return -1; } @@ -1102,7 +1103,7 @@ if(cond_init(&sock->data.send_cv)) { errno = ENOBUFS; mutex_unlock(&sock->mutex); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); free(sock->data.sndbuf); free(sock->data.rcvbuf); return -1; @@ -1111,7 +1112,7 @@ if(cond_init(&sock->data.recv_cv)) { errno = ENOBUFS; mutex_unlock(&sock->mutex); - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); cond_destroy(&sock->data.send_cv); free(sock->data.sndbuf); free(sock->data.rcvbuf); @@ -1128,13 +1129,13 @@ /* Send a <SYN> packet */ if(tcp_send_syn(sock, 0) == -1) { - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); mutex_unlock(&sock->mutex); return -1; } /* Release the write lock... */ - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); /* Now, lets see if this is socket is non-blocking... */ if(sock->flags & FS_SOCKET_NONBLOCK || irq_inside_int()) { @@ -1174,17 +1175,17 @@ backlog = 1; if(irq_inside_int()) { - if(rwsem_read_trylock(tcp_sem)) { + if(rwsem_read_trylock(&tcp_sem)) { errno = EWOULDBLOCK; return -1; } } else { - rwsem_read_lock(tcp_sem); + rwsem_read_lock(&tcp_sem); } if(!(sock = (struct tcp_sock *)hnd->data)) { - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = EBADF; return -1; } @@ -1193,7 +1194,7 @@ in here... */ if(irq_inside_int()) { if(mutex_trylock(&sock->mutex)) { - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = EWOULDBLOCK; return -1; } @@ -1206,7 +1207,7 @@ actually move it to the listening state */ if(sock->state != TCP_STATE_CLOSED) { mutex_unlock(&sock->mutex); - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = EINVAL; return -1; } @@ -1214,7 +1215,7 @@ /* Make sure the socket has been bound */ if(!sock->local_addr.sin6_port) { mutex_unlock(&sock->mutex); - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = EDESTADDRREQ; return -1; } @@ -1224,7 +1225,7 @@ if(!sock->listen.queue) { mutex_unlock(&sock->mutex); - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = ENOBUFS; return -1; } @@ -1233,7 +1234,7 @@ free(sock->listen.queue); sock->listen.queue = NULL; mutex_unlock(&sock->mutex); - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = ENOBUFS; return -1; } @@ -1244,7 +1245,7 @@ /* We're done now, clean up the locks */ mutex_unlock(&sock->mutex); - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); return 0; } @@ -1265,17 +1266,17 @@ } if(irq_inside_int()) { - if(rwsem_read_trylock(tcp_sem)) { + if(rwsem_read_trylock(&tcp_sem)) { errno = EWOULDBLOCK; return -1; } } else { - rwsem_read_lock(tcp_sem); + rwsem_read_lock(&tcp_sem); } if(!(sock = (struct tcp_sock *)hnd->data)) { - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = EBADF; return -1; } @@ -1284,7 +1285,7 @@ in here... */ if(irq_inside_int()) { if(mutex_trylock(&sock->mutex)) { - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = EWOULDBLOCK; return -1; } @@ -1293,7 +1294,7 @@ mutex_lock(&sock->mutex); } - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); /* Make sure they haven't shut down the socket... */ if(sock->flags & (SHUT_RD << 24)) { @@ -1424,17 +1425,17 @@ } if(irq_inside_int()) { - if(rwsem_read_trylock(tcp_sem)) { + if(rwsem_read_trylock(&tcp_sem)) { errno = EWOULDBLOCK; return -1; } } else { - rwsem_read_lock(tcp_sem); + rwsem_read_lock(&tcp_sem); } if(!(sock = (struct tcp_sock *)hnd->data)) { - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = EBADF; return -1; } @@ -1443,7 +1444,7 @@ in here... */ if(irq_inside_int()) { if(mutex_trylock(&sock->mutex)) { - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = EWOULDBLOCK; return -1; } @@ -1452,7 +1453,7 @@ mutex_lock(&sock->mutex); } - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); /* Check if the socket has been shut down for writing. */ if(sock->flags & (SHUT_WR << 24)) { @@ -1560,24 +1561,24 @@ struct tcp_sock *sock; if(irq_inside_int()) { - if(rwsem_read_trylock(tcp_sem)) { + if(rwsem_read_trylock(&tcp_sem)) { errno = EWOULDBLOCK; return -1; } } else { - rwsem_read_lock(tcp_sem); + rwsem_read_lock(&tcp_sem); } if(!(sock = (struct tcp_sock *)hnd->data)) { - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = EBADF; return -1; } if(irq_inside_int()) { if(mutex_trylock(&sock->mutex)) { - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = EWOULDBLOCK; return -1; } @@ -1588,7 +1589,7 @@ if(how & 0xFFFFFFFC) { mutex_unlock(&sock->mutex); - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = EINVAL; return -1; } @@ -1596,7 +1597,7 @@ sock->flags |= (how << 24); mutex_unlock(&sock->mutex); - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); return 0; } @@ -1612,24 +1613,24 @@ } if(irq_inside_int()) { - if(rwsem_read_trylock(tcp_sem)) { + if(rwsem_read_trylock(&tcp_sem)) { errno = EWOULDBLOCK; return -1; } } else { - rwsem_read_lock(tcp_sem); + rwsem_read_lock(&tcp_sem); } if(!(sock = (struct tcp_sock *)hnd->data)) { - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = EBADF; return -1; } if(irq_inside_int()) { if(mutex_trylock(&sock->mutex)) { - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = EWOULDBLOCK; return -1; } @@ -1692,18 +1693,17 @@ /* If it wasn't handled, return that error. */ mutex_unlock(&sock->mutex); - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = ENOPROTOOPT; return -1; ret_inval: mutex_unlock(&sock->mutex); - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = EINVAL; return -1; copy_int: - if(*option_len >= sizeof(int)) { memcpy(option_value, &tmp, sizeof(int)); *option_len = sizeof(int); @@ -1713,7 +1713,7 @@ } mutex_unlock(&sock->mutex); - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); return 0; } @@ -1728,24 +1728,24 @@ } if(irq_inside_int()) { - if(rwsem_read_trylock(tcp_sem)) { + if(rwsem_read_trylock(&tcp_sem)) { errno = EWOULDBLOCK; return -1; } } else { - rwsem_read_lock(tcp_sem); + rwsem_read_lock(&tcp_sem); } if(!(sock = (struct tcp_sock *)hnd->data)) { - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = EBADF; return -1; } if(irq_inside_int()) { if(mutex_trylock(&sock->mutex)) { - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = EWOULDBLOCK; return -1; } @@ -1833,19 +1833,19 @@ /* If it wasn't handled, return that error. */ mutex_unlock(&sock->mutex); - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = ENOPROTOOPT; return -1; ret_inval: mutex_unlock(&sock->mutex); - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = EINVAL; return -1; ret_success: mutex_unlock(&sock->mutex); - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); return 0; } @@ -1856,17 +1856,17 @@ long val; if(irq_inside_int()) { - if(rwsem_read_trylock(tcp_sem)) { + if(rwsem_read_trylock(&tcp_sem)) { errno = EWOULDBLOCK; return -1; } } else { - rwsem_read_lock(tcp_sem); + rwsem_read_lock(&tcp_sem); } if(!(sock = (struct tcp_sock *)hnd->data)) { - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = EBADF; return -1; } @@ -1875,7 +1875,7 @@ in here... */ if(irq_inside_int()) { if(mutex_trylock(&sock->mutex)) { - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); errno = EWOULDBLOCK; return -1; } @@ -1914,7 +1914,7 @@ out: mutex_unlock(&sock->mutex); - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); return rv; } @@ -1923,24 +1923,24 @@ short rv = 0; if(irq_inside_int()) { - if(rwsem_read_trylock(tcp_sem)) { + if(rwsem_read_trylock(&tcp_sem)) { return 0; } } else { - rwsem_read_lock(tcp_sem); + rwsem_read_lock(&tcp_sem); } /* Lock the mutex on the socket itself first. We need to pull some data from it that doesn't affect the rest of the list, so let's start there... */ if(!(sock = (struct tcp_sock *)hnd->data)) { - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); return POLLNVAL; } if(irq_inside_int()) { if(mutex_trylock(&sock->mutex)) { - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); return 0; } } @@ -1990,7 +1990,7 @@ } mutex_unlock(&sock->mutex); - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); return rv & (events | POLLHUP | POLLERR); } @@ -2785,19 +2785,19 @@ flags = ntohs(tcp->off_flags); if(irq_inside_int()) { - if(rwsem_read_trylock(tcp_sem)) { + if(rwsem_read_trylock(&tcp_sem)) { return -1; } } else { - rwsem_read_lock(tcp_sem); + rwsem_read_lock(&tcp_sem); } /* Find a matching socket */ if((s = find_sock(&srca, &dsta, tcp->src_port, tcp->dst_port, domain))) { /* Make sure we take care of busy sockets... */ if(s == (struct tcp_sock *) - 1) { - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); return 0; } @@ -2832,7 +2832,7 @@ mutex_unlock(&s->mutex); } - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); /* If we get in here, something went wrong... Send a RST. */ if(rv && !(flags & TCP_FLAG_RST)) { @@ -2846,7 +2846,7 @@ struct tcp_sock *i, *tmp; uint64_t timer; - rwsem_read_lock(tcp_sem); + rwsem_read_lock(&tcp_sem); LIST_FOREACH(i, &tcp_socks, sock_list) { mutex_lock(&i->mutex); @@ -2916,10 +2916,10 @@ mutex_unlock(&i->mutex); } - rwsem_read_unlock(tcp_sem); + rwsem_read_unlock(&tcp_sem); /* Go through and clean up any sockets that need to be destroyed. */ - rwsem_write_lock(tcp_sem); + rwsem_write_lock(&tcp_sem); i = LIST_FIRST(&tcp_socks); @@ -2940,7 +2940,7 @@ i = tmp; } - rwsem_write_unlock(tcp_sem); + rwsem_write_unlock(&tcp_sem); } /* Protocol handler for fs_socket. */ @@ -2966,14 +2966,9 @@ }; int net_tcp_init() { - if(!(tcp_sem = rwsem_create())) + if((thd_cb_id = net_thd_add_callback(tcp_thd_cb, NULL, 50)) < 0) return -1; - if((thd_cb_id = net_thd_add_callback(tcp_thd_cb, NULL, 50)) < 0) { - rwsem_destroy(tcp_sem); - return -1; - } - return fs_socket_proto_add(&proto); } @@ -3015,10 +3010,5 @@ /* Remove us from fs_socket and clean up the semaphore */ fs_socket_proto_remove(&proto); - if(tcp_sem) { - rwsem_destroy(tcp_sem); - tcp_sem = NULL; - } - irq_restore(old); } Modified: kos/kernel/thread/rwsem.c =================================================================== --- kos/kernel/thread/rwsem.c 2012-06-10 18:28:57 UTC (rev 813) +++ kos/kernel/thread/rwsem.c 2012-06-10 18:29:40 UTC (rev 814) @@ -1,82 +1,104 @@ /* KallistiOS ##version## rwsem.c - Copyright (C) 2008 Lawrence Sebald + Copyright (C) 2008, 2012 Lawrence Sebald */ /* Defines reader/writer semaphores */ -#include <malloc.h> #include <stdio.h> -#include <assert.h> +#include <stdlib.h> #include <errno.h> -#include <sys/queue.h> #include <kos/rwsem.h> #include <kos/genwait.h> -#include <arch/spinlock.h> -/* Reader/writer semaphore list spinlock */ -static spinlock_t mutex; - -/* Global list of reader/writer semaphores */ -static struct rwsemlist rwsem_list; - /* Allocate a new reader/writer semaphore */ rw_semaphore_t *rwsem_create() { rw_semaphore_t *s; - s = (rw_semaphore_t *)malloc(sizeof(rw_semaphore_t)); + dbglog(DBG_WARNING, "Creating reader/writer semaphore with deprecated " + "rwsem_create(). Please update your code!\n"); - if(!s) { + if(!(s = (rw_semaphore_t *)malloc(sizeof(rw_semaphore_t)))) { errno = ENOMEM; return NULL; } + s->initialized = 2; s->read_count = 0; - s->write_lock = 0; + s->write_lock = NULL; + s->reader_waiting = NULL; - spinlock_lock(&mutex); - LIST_INSERT_HEAD(&rwsem_list, s, list); - spinlock_unlock(&mutex); - return s; } +int rwsem_init(rw_semaphore_t *s) { + s->initialized = 1; + s->read_count = 0; + s->write_lock = NULL; + s->reader_waiting = NULL; + + return 0; +} + /* Destroy a reader/writer semaphore */ -void rwsem_destroy(rw_semaphore_t *s) { - /* XXXX: Should really cause anyone waiting to get an error back on their - wait... hmm. */ - spinlock_lock(&mutex); - LIST_REMOVE(s, list); - spinlock_unlock(&mutex); +int rwsem_destroy(rw_semaphore_t *s) { + int rv = 0, old; - free(s); + old = irq_disable(); + + if(s->read_count || s->write_lock) { + errno = EBUSY; + rv = -1; + } + else if(s->initialized == 2) { + free(s); + } + else { + s->initialized = 0; + } + + irq_restore(old); + return rv; } /* Lock a reader/writer semaphore for reading */ -int rwsem_read_lock(rw_semaphore_t *s) { +int rwsem_read_lock_timed(rw_semaphore_t *s, int timeout) { int old, rv = 0; if(irq_inside_int()) { - dbglog(DBG_WARNING, "rwsem_read_lock: called inside interrupt\n"); + dbglog(DBG_WARNING, "rwsem_read_lock_timed: called inside interrupt\n"); errno = EPERM; return -1; } + if(timeout < 0) { + errno = EINVAL; + return -1; + } + old = irq_disable(); + if(s->initialized != 1 && s->initialized != 2) { + irq_restore(old); + errno = EINVAL; + return -1; + } + /* If the write lock is not held, let the thread proceed */ if(!s->write_lock) { ++s->read_count; } else { /* Block until the write lock is not held any more */ - rv = genwait_wait(s, "rwsem_read_lock", 0, NULL); + rv = genwait_wait(s, timeout ? "rwsem_read_lock_timed" : + "rwsem_read_lock", timeout, NULL); if(rv < 0) { - assert(errno == EINTR); rv = -1; + if(errno == EAGAIN) + errno = ETIMEDOUT; } else { ++s->read_count; @@ -87,34 +109,52 @@ return rv; } +int rwsem_read_lock(rw_semaphore_t *s) { + return rwsem_read_lock_timed(s, 0); +} + /* Lock a reader/writer semaphore for writing */ -int rwsem_write_lock(rw_semaphore_t *s) { +int rwsem_write_lock_timed(rw_semaphore_t *s, int timeout) { int old, rv = 0; if(irq_inside_int()) { - dbglog(DBG_WARNING, "rwsem_write_lock: called inside interrupt\n"); + dbglog(DBG_WARNING, "rwsem_write_lock_timed: called inside " + "interrupt\n"); errno = EPERM; return -1; } + if(timeout < 0) { + errno = EINVAL; + return -1; + } + old = irq_disable(); + if(s->initialized != 1 && s->initialized != 2) { + irq_restore(old); + errno = EINVAL; + return -1; + } + /* If the write lock is not held and there are no readers in their critical sections, let the thread proceed. */ if(!s->write_lock && !s->read_count) { - s->write_lock = 1; + s->write_lock = thd_current; } else { /* Block until the write lock is not held and there are no readers inside their critical sections */ - rv = genwait_wait(&s->write_lock, "rwsem_write_lock", 0, NULL); + rv = genwait_wait(&s->write_lock, timeout ? "rwsem_write_lock_timed" : + "rwsem_write_lock", timeout, NULL); if(rv < 0) { - assert(errno == EINTR); rv = -1; + if(errno == EAGAIN) + errno = ETIMEDOUT; } else { - s->write_lock = 1; + s->write_lock = thd_current; } } @@ -122,17 +162,39 @@ return rv; } +int rwsem_write_lock(rw_semaphore_t *s) { + return rwsem_write_lock_timed(s, 0); +} + /* Unlock a reader/writer semaphore from a read lock. */ int rwsem_read_unlock(rw_semaphore_t *s) { int old; old = irq_disable(); + if(s->initialized != 1 && s->initialized != 2) { + irq_restore(old); + errno = EINVAL; + return -1; + } + + if(!s->read_count) { + irq_restore(old); + errno = EPERM; + return -1; + } + --s->read_count; /* If this was the last reader, attempt to wake any writers waiting. */ if(!s->read_count) { - genwait_wake_one(&s->write_lock); + if(s->reader_waiting) { + genwait_wake_thd(&s->write_lock, s->reader_waiting, 0); + s->reader_waiting = NULL; + } + else { + genwait_wake_one(&s->write_lock); + } } irq_restore(old); @@ -146,8 +208,20 @@ old = irq_disable(); - s->write_lock = 0; + if(s->initialized != 1 && s->initialized != 2) { + irq_restore(old); + errno = EINVAL; + return -1; + } + if(s->write_lock != thd_current) { + irq_restore(old); + errno = EPERM; + return -1; + } + + s->write_lock = NULL; + /* Give writers priority, attempt to wake any writers first. */ woken = genwait_wake_cnt(&s->write_lock, 1, 0); @@ -161,14 +235,44 @@ return 0; } +int rwsem_unlock(rw_semaphore_t *s) { + int old, rv; + + old = irq_disable(); + + if(s->initialized != 1 && s->initialized != 2) { + errno = EINVAL; + rv = -1; + } + else if(!s->write_lock && !s->read_count) { + errno = EPERM; + rv = -1; + } + /* Is this thread holding the write lock? */ + else if(s->write_lock == thd_current) { + rv = rwsem_write_unlock(s); + } + /* Not holding the write lock, assume its holding the read lock... */ + else { + rv = rwsem_read_unlock(s); + } + + irq_restore(old); + return rv; +} + /* Attempt to lock a reader/writer semaphore for reading, but do not block. */ int rwsem_read_trylock(rw_semaphore_t *s) { int old, rv; old = irq_disable(); - + + if(s->initialized != 1 && s->initialized != 2) { + rv = -1; + errno = EINVAL; + } /* Is the write lock held? */ - if(s->write_lock) { + else if(s->write_lock) { rv = -1; errno = EWOULDBLOCK; } @@ -187,15 +291,19 @@ old = irq_disable(); + if(s->initialized != 1 && s->initialized != 2) { + rv = -1; + errno = EINVAL; + } /* Are there any readers in their critical sections, or is the write lock already held, if so we can't do anything about that now. */ - if(s->read_count || s->write_lock) { + else if(s->read_count || s->write_lock) { rv = -1; errno = EWOULDBLOCK; } else { rv = 0; - s->write_lock = 1; + s->write_lock = thd_current; } irq_restore(old); @@ -203,53 +311,92 @@ } /* "Upgrade" a read lock to a write lock. */ -int rwsem_read_upgrade(rw_semaphore_t *s) { +int rwsem_read_upgrade_timed(rw_semaphore_t *s, int timeout) { int old, rv = 0; if(irq_inside_int()) { - dbglog(DBG_WARNING, "rwsem_read_upgrade: called inside interrupt\n"); + dbglog(DBG_WARNING, "rwsem_read_upgrade_timed: called inside " + "interrupt\n"); errno = EPERM; return -1; } + if(timeout < 0) { + errno = EINVAL; + return -1; + } + old = irq_disable(); - --s->read_count; - - /* If there are still other readers, wait patiently for our turn. */ - if(s->read_count) { - rv = genwait_wait(&s->write_lock, "rwsem_read_upgrade", 0, NULL); - - if(rv < 0) { - assert(errno == EINTR); + if(s->initialized != 1 && s->initialized != 2) { + rv = -1; + errno = EINVAL; + } + /* If there are still other readers, see if any other readers have tried to + upgrade or not... */ + else if(s->read_count > 1) { + if(s->reader_waiting) { + /* We've got someone ahead of us, so there's really not anything + that can be done at this point... */ rv = -1; + errno = EBUSY; } else { - s->write_lock = 1; + --s->read_count; + s->reader_waiting = thd_current; + rv = genwait_wait(&s->write_lock, timeout ? + "rwsem_read_upgrade_timed" : "rwsem_read_upgrade", + timeout, NULL); + + if(rv < 0) { + /* The only way we can error out is if there are still readers + with the lock, so we can safely re-grab the lock here. */ + ++s->read_count; + rv = -1; + + if(errno == EAGAIN) + errno = ETIMEDOUT; + } + else { + s->write_lock = thd_current; + } } } else { - s->write_lock = 1; + s->read_count = 0; + s->write_lock = thd_current; } irq_restore(old); return rv; } +int rwsem_read_upgrade(rw_semaphore_t *s) { + return rwsem_read_upgrade_timed(s, 0); +} + /* Attempt to upgrade a read lock to a write lock, but do not block. */ int rwsem_read_tryupgrade(rw_semaphore_t *s) { int old, rv; old = irq_disable(); - if(s->read_count != 1) { + if(s->initialized != 1 && s->initialized != 2) { rv = -1; + errno = EINVAL; + } + else if(s->reader_waiting) { + rv = -1; + errno = EBUSY; + } + else if(s->read_count != 1) { + rv = -1; errno = EWOULDBLOCK; } else { rv = 0; s->read_count = 0; - s->write_lock = 1; + s->write_lock = thd_current; } irq_restore(old); @@ -263,17 +410,5 @@ /* Return the current status of the write lock */ int rwsem_write_locked(rw_semaphore_t *s) { - return s->write_lock; + return !!s->write_lock; } - -/* Initialize reader/writer semaphores */ -int rwsem_init() { - LIST_INIT(&rwsem_list); - spinlock_init(&mutex); - return 0; -} - -/* Shut down reader/writer semaphores */ -void rwsem_shutdown() { - /* XXXX: Do something useful */ -} Modified: kos/kernel/thread/thread.c =================================================================== --- kos/kernel/thread/thread.c 2012-06-10 18:28:57 UTC (rev 813) +++ kos/kernel/thread/thread.c 2012-06-10 18:29:40 UTC (rev 814) @@ -869,7 +869,6 @@ /* Initialize thread sync primitives */ genwait_init(); - rwsem_init(); sem_init(); /* Setup our pre-emption handler */ @@ -908,7 +907,6 @@ } /* Shutdown thread sync primitives */ - rwsem_shutdown(); sem_shutdown(); genwait_shutdown(); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |