source: trunk/libtransmission/platform.c @ 2343

Last change on this file since 2343 was 2343, checked in by joshe, 14 years ago

Change a couple functions to take an in_addr pointer instead of an in_addr.
Forward declare struct in_addr and include the relevant headers in the .c files where it's used.

  • Property svn:keywords set to Date Rev Author Id
File size: 19.0 KB
Line 
1/******************************************************************************
2 * $Id: platform.c 2343 2007-07-14 16:29:21Z joshe $
3 *
4 * Copyright (c) 2005 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#ifdef SYS_BEOS
26  #include <fs_info.h>
27  #include <FindDirectory.h>
28#endif
29#include <sys/types.h>
30#include <dirent.h>
31#include <fcntl.h>
32#include <unistd.h> /* getuid getpid close */
33#include "transmission.h"
34
35#if !defined( SYS_BEOS ) && !defined( __AMIGAOS4__ )
36
37#include <pwd.h>
38
39const char *
40tr_getHomeDirectory( void )
41{
42    static char     homeDirectory[MAX_PATH_LENGTH];
43    static int      init = 0;
44    char          * envHome;
45    struct passwd * pw;
46
47    if( init )
48    {
49        return homeDirectory;
50    }
51
52    envHome = getenv( "HOME" );
53    if( NULL == envHome )
54    {
55        pw = getpwuid( getuid() );
56        endpwent();
57        if( NULL == pw )
58        {
59            /* XXX need to handle this case */
60            return NULL;
61        }
62        envHome = pw->pw_dir;
63    }
64
65    snprintf( homeDirectory, MAX_PATH_LENGTH, "%s", envHome );
66    init = 1;
67
68    return homeDirectory;
69}
70
71#else
72
73const char *
74tr_getHomeDirectory( void )
75{
76    /* XXX */
77    return "";
78}
79
80#endif /* !SYS_BEOS && !__AMIGAOS4__ */
81
82static void
83tr_migrateResume( const char *oldDirectory, const char *newDirectory )
84{
85    DIR * dirh = opendir( oldDirectory );
86
87    if( dirh != NULL )
88    {
89        struct dirent * dirp;
90
91        while( ( dirp = readdir( dirh ) ) )
92        {
93            if( !strncmp( "resume.", dirp->d_name, 7 ) )
94            {
95                char o[MAX_PATH_LENGTH];
96                char n[MAX_PATH_LENGTH];
97                tr_buildPath( o, sizeof(o), oldDirectory, dirp->d_name, NULL );
98                tr_buildPath( n, sizeof(n), newDirectory, dirp->d_name, NULL );
99                rename( o, n );
100            }
101        }
102
103        closedir( dirh );
104    }
105}
106
107const char *
108tr_getPrefsDirectory( void )
109{
110    static char   buf[MAX_PATH_LENGTH];
111    static int    init = 0;
112    static size_t buflen = sizeof(buf);
113    const char* h;
114
115    if( init )
116        return buf;
117
118    h = tr_getHomeDirectory();
119#ifdef SYS_BEOS
120    find_directory( B_USER_SETTINGS_DIRECTORY,
121                    dev_for_path("/boot"), true, buf, buflen );
122    strcat( buf, "/Transmission" );
123#elif defined( SYS_DARWIN )
124    tr_buildPath ( buf, buflen, h,
125                  "Library", "Application Support", "Transmission", NULL );
126#elif defined(__AMIGAOS4__)
127    snprintf( buf, buflen, "PROGDIR:.transmission" );
128#else
129    tr_buildPath ( buf, buflen, h, ".transmission", NULL );
130#endif
131
132    tr_mkdir( buf );
133    init = 1;
134
135#ifdef SYS_DARWIN
136    char old[MAX_PATH_LENGTH];
137    tr_buildPath ( old, sizeof(old), h, ".transmission", NULL );
138    tr_migrateResume( old, buf );
139    rmdir( old );
140#endif
141
142    return buf;
143}
144
145const char *
146tr_getCacheDirectory( void )
147{
148    static char buf[MAX_PATH_LENGTH];
149    static int  init = 0;
150    static const size_t buflen = sizeof(buf);
151    const char * p;
152
153    if( init )
154        return buf;
155
156    p = tr_getPrefsDirectory();
157#ifdef SYS_BEOS
158    tr_buildPath( buf, buflen, p, "Cache", NULL );
159#elif defined( SYS_DARWIN )
160    tr_buildPath( buf, buflen, tr_getHomeDirectory(),
161                  "Library", "Caches", "Transmission", NULL );
162#else
163    tr_buildPath( buf, buflen, p, "cache", NULL );
164#endif
165
166    tr_mkdir( buf );
167    init = 1;
168
169    if( strcmp( p, buf ) )
170        tr_migrateResume( p, buf );
171
172    return buf;
173}
174
175const char *
176tr_getTorrentsDirectory( void )
177{
178    static char buf[MAX_PATH_LENGTH];
179    static int  init = 0;
180    static const size_t buflen = sizeof(buf);
181    const char * p;
182
183    if( init )
184        return buf;
185
186    p = tr_getPrefsDirectory ();
187
188#ifdef SYS_BEOS
189    tr_buildPath( buf, buflen, p, "Torrents", NULL );
190#elif defined( SYS_DARWIN )
191    tr_buildPath( buf, buflen, p, "Torrents", NULL );
192#else
193    tr_buildPath( buf, buflen, p, "torrents", NULL );
194#endif
195
196    tr_mkdir( buf );
197    init = 1;
198    return buf;
199}
200
201static void ThreadFunc( void * _t )
202{
203    tr_thread_t * t = _t;
204    char* name = tr_strdup( t->name );
205
206#ifdef SYS_BEOS
207    /* This is required because on BeOS, SIGINT is sent to each thread,
208       which kills them not nicely */
209    signal( SIGINT, SIG_IGN );
210#endif
211
212    tr_dbg( "Thread '%s' started", name );
213    t->func( t->arg );
214    tr_dbg( "Thread '%s' exited", name );
215    tr_free( name );
216}
217
218void tr_threadCreate( tr_thread_t * t,
219                      void (*func)(void *), void * arg,
220                      const char * name )
221{
222    t->func = func;
223    t->arg  = arg;
224    t->name = tr_strdup( name );
225#ifdef SYS_BEOS
226    t->thread = spawn_thread( (void *) ThreadFunc, name,
227                              B_NORMAL_PRIORITY, t );
228    resume_thread( t->thread );
229#else
230    pthread_create( &t->thread, NULL, (void *) ThreadFunc, t );
231#endif
232}
233
234const tr_thread_t THREAD_EMPTY = { NULL, NULL, NULL, 0 };
235
236void tr_threadJoin( tr_thread_t * t )
237{
238    if( t->func != NULL )
239    {
240#ifdef SYS_BEOS
241        long exit;
242        wait_for_thread( t->thread, &exit );
243#else
244        pthread_join( t->thread, NULL );
245#endif
246        tr_dbg( "Thread '%s' joined", t->name );
247        tr_free( t->name );
248        t->name = NULL;
249        t->func = NULL;
250    }
251}
252
253void tr_lockInit( tr_lock_t * l )
254{
255#ifdef SYS_BEOS
256    *l = create_sem( 1, "" );
257#else
258    pthread_mutex_init( l, NULL );
259#endif
260}
261
262void tr_lockClose( tr_lock_t * l )
263{
264#ifdef SYS_BEOS
265    delete_sem( *l );
266#else
267    pthread_mutex_destroy( l );
268#endif
269}
270
271int tr_lockTryLock( tr_lock_t * l )
272{
273#ifdef SYS_BEOS
274    return acquire_sem_etc( *l, 1, B_RELATIVE_TIMEOUT, 0 );
275#else
276    /* success on zero! */
277    return pthread_mutex_trylock( l );
278#endif
279}
280
281void tr_lockLock( tr_lock_t * l )
282{
283#ifdef SYS_BEOS
284    acquire_sem( *l );
285#else
286    pthread_mutex_lock( l );
287#endif
288}
289
290void tr_lockUnlock( tr_lock_t * l )
291{
292#ifdef SYS_BEOS
293    release_sem( *l );
294#else
295    pthread_mutex_unlock( l );
296#endif
297}
298
299
300void tr_condInit( tr_cond_t * c )
301{
302#ifdef SYS_BEOS
303    c->sem = create_sem( 1, "" );
304    c->start = 0;
305    c->end = 0;
306#else
307    pthread_cond_init( c, NULL );
308#endif
309}
310
311void tr_condWait( tr_cond_t * c, tr_lock_t * l )
312{
313#ifdef SYS_BEOS
314    /* Keep track of that thread */
315    acquire_sem( c->sem );
316    c->threads[c->end] = find_thread( NULL );
317    c->end = ( c->end + 1 ) % BEOS_MAX_THREADS;
318    assert( c->end != c->start ); /* We hit BEOS_MAX_THREADS, arggh */
319    release_sem( c->sem );
320
321    release_sem( *l );
322    suspend_thread( find_thread( NULL ) ); /* Wait for signal */
323    acquire_sem( *l );
324#else
325    pthread_cond_wait( c, l );
326#endif
327}
328
329#ifdef SYS_BEOS
330static int condTrySignal( tr_cond_t * c )
331{
332    if( c->start == c->end )
333        return 1;
334
335    for( ;; )
336    {
337        thread_info info;
338        get_thread_info( c->threads[c->start], &info );
339        if( info.state == B_THREAD_SUSPENDED )
340        {
341            resume_thread( c->threads[c->start] );
342            c->start = ( c->start + 1 ) % BEOS_MAX_THREADS;
343            break;
344        }
345        /* The thread is not suspended yet, which can happen since
346         * tr_condWait does not atomically suspends after releasing
347         * the semaphore. Wait a bit and try again. */
348        snooze( 5000 );
349    }
350    return 0;
351}
352#endif
353void tr_condSignal( tr_cond_t * c )
354{
355#ifdef SYS_BEOS
356    acquire_sem( c->sem );
357    condTrySignal( c );
358    release_sem( c->sem );
359#else
360    pthread_cond_signal( c );
361#endif
362}
363void tr_condBroadcast( tr_cond_t * c )
364{
365#ifdef SYS_BEOS
366    acquire_sem( c->sem );
367    while( !condTrySignal( c ) );
368    release_sem( c->sem );
369#else
370    pthread_cond_broadcast( c );
371#endif
372}
373
374void tr_condClose( tr_cond_t * c )
375{
376#ifdef SYS_BEOS
377    delete_sem( c->sem );
378#else
379    pthread_cond_destroy( c );
380#endif
381}
382
383
384#if defined( BSD )
385
386#include <sys/types.h>
387#include <sys/socket.h>
388#include <netinet/in.h>
389#include <arpa/inet.h>
390#include <sys/sysctl.h>
391#include <net/route.h>
392
393static uint8_t *
394getroute( int * buflen );
395static int
396parseroutes( uint8_t * buf, int len, struct in_addr * addr );
397
398int
399tr_getDefaultRoute( struct in_addr * addr )
400{
401    uint8_t * buf;
402    int len;
403
404    buf = getroute( &len );
405    if( NULL == buf )
406    {
407        tr_err( "failed to get default route (BSD)" );
408        return 1;
409    }
410
411    len = parseroutes( buf, len, addr );
412    free( buf );
413
414    return len;
415}
416
417#ifndef SA_SIZE
418#define ROUNDUP( a, size ) \
419    ( ( (a) & ( (size) - 1 ) ) ? ( 1 + ( (a) | ( (size) - 1 ) ) ) : (a) )
420#define SA_SIZE( sap ) \
421    ( sap->sa_len ? ROUNDUP( (sap)->sa_len, sizeof( u_long ) ) : \
422                    sizeof( u_long ) )
423#endif /* !SA_SIZE */
424#define NEXT_SA( sap ) \
425    (struct sockaddr *) ( (caddr_t) (sap) + ( SA_SIZE( (sap) ) ) )
426
427static uint8_t *
428getroute( int * buflen )
429{
430    int     mib[6];
431    size_t  len;
432    uint8_t * buf;
433
434    mib[0] = CTL_NET;
435    mib[1] = PF_ROUTE;
436    mib[2] = 0;
437    mib[3] = AF_INET;
438    mib[4] = NET_RT_FLAGS;
439    mib[5] = RTF_GATEWAY;
440
441    if( sysctl( mib, 6, NULL, &len, NULL, 0 ) )
442    {
443        if( ENOENT != errno )
444        {
445            tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
446                    strerror( errno ) );
447        }
448        *buflen = 0;
449        return NULL;
450    }
451
452    buf = malloc( len );
453    if( NULL == buf )
454    {
455        *buflen = 0;
456        return NULL;
457    }
458
459    if( sysctl( mib, 6, buf, &len, NULL, 0 ) )
460    {
461        tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
462                strerror( errno ) );
463        free( buf );
464        *buflen = 0;
465        return NULL;
466    }
467
468    *buflen = len;
469
470    return buf;
471}
472
473static int
474parseroutes( uint8_t * buf, int len, struct in_addr * addr )
475{
476    uint8_t            * end;
477    struct rt_msghdr   * rtm;
478    struct sockaddr    * sa;
479    struct sockaddr_in * sin;
480    int                  ii;
481    struct in_addr       dest, gw;
482
483    end = buf + len;
484    while( end > buf + sizeof( *rtm ) )
485    {
486        rtm = (struct rt_msghdr *) buf;
487        buf += rtm->rtm_msglen;
488        if( end >= buf )
489        {
490            dest.s_addr = INADDR_NONE;
491            gw.s_addr   = INADDR_NONE;
492            sa = (struct sockaddr *) ( rtm + 1 );
493
494            for( ii = 0; ii < RTAX_MAX && (uint8_t *) sa < buf; ii++ )
495            {
496                if( buf < (uint8_t *) NEXT_SA( sa ) )
497                {
498                    break;
499                }
500
501                if( rtm->rtm_addrs & ( 1 << ii ) )
502                {
503                    if( AF_INET == sa->sa_family )
504                    {
505                        sin = (struct sockaddr_in *) sa;
506                        switch( ii )
507                        {
508                            case RTAX_DST:
509                                dest = sin->sin_addr;
510                                break;
511                            case RTAX_GATEWAY:
512                                gw = sin->sin_addr;
513                                break;
514                        }
515                    }
516                    sa = NEXT_SA( sa );
517                }
518            }
519
520            if( INADDR_ANY == dest.s_addr && INADDR_NONE != gw.s_addr )
521            {
522                *addr = gw;
523                return 0;
524            }
525        }
526    }
527
528    return 1;
529}
530
531#elif defined( linux ) || defined( __linux ) || defined( __linux__ )
532
533#include <linux/types.h>
534#include <linux/netlink.h>
535#include <linux/rtnetlink.h>
536
537#define SEQNUM 195909
538
539static int
540getsock( void );
541static uint8_t *
542getroute( int fd, unsigned int * buflen );
543static int
544parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr );
545
546int
547tr_getDefaultRoute( struct in_addr * addr )
548{
549    int fd, ret;
550    unsigned int len;
551    uint8_t * buf;
552
553    ret = 1;
554    fd = getsock();
555    if( 0 <= fd )
556    {
557        while( ret )
558        {
559            buf = getroute( fd, &len );
560            if( NULL == buf )
561            {
562                break;
563            }
564            ret = parseroutes( buf, len, addr );
565            free( buf );
566        }
567        close( fd );
568    }
569
570    if( ret )
571    {
572        tr_err( "failed to get default route (Linux)" );
573    }
574
575    return ret;
576}
577
578static int
579getsock( void )
580{
581    int fd, flags;
582    struct
583    {
584        struct nlmsghdr nlh;
585        struct rtgenmsg rtg;
586    } req;
587    struct sockaddr_nl snl;
588
589    fd = socket( PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE );
590    if( 0 > fd )
591    {
592        tr_err( "failed to create routing socket (%s)", strerror( errno ) );
593        return -1;
594    }
595
596    flags = fcntl( fd, F_GETFL );
597    if( 0 > flags || 0 > fcntl( fd, F_SETFL, O_NONBLOCK | flags ) )
598    {
599        tr_err( "failed to set socket nonblocking (%s)", strerror( errno ) );
600        close( fd );
601        return -1;
602    }
603
604    bzero( &snl, sizeof( snl ) );
605    snl.nl_family = AF_NETLINK;
606
607    bzero( &req, sizeof( req ) );
608    req.nlh.nlmsg_len = NLMSG_LENGTH( sizeof( req.rtg ) );
609    req.nlh.nlmsg_type = RTM_GETROUTE;
610    req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
611    req.nlh.nlmsg_seq = SEQNUM;
612    req.nlh.nlmsg_pid = 0;
613    req.rtg.rtgen_family = AF_INET;
614
615    if( 0 > sendto( fd, &req, sizeof( req ), 0,
616                    (struct sockaddr *) &snl, sizeof( snl ) ) )
617    {
618        tr_err( "failed to write to routing socket (%s)", strerror( errno ) );
619        close( fd );
620        return -1;
621    }
622
623    return fd;
624}
625
626static uint8_t *
627getroute( int fd, unsigned int * buflen )
628{
629    void             * buf;
630    unsigned int       len;
631    ssize_t            res;
632    struct sockaddr_nl snl;
633    socklen_t          slen;
634
635    len = 8192;
636    buf = calloc( 1, len );
637    if( NULL == buf )
638    {
639        *buflen = 0;
640        return NULL;
641    }
642
643    for( ;; )
644    {
645        bzero( &snl, sizeof( snl ) );
646        slen = sizeof( snl );
647        res = recvfrom( fd, buf, len, 0, (struct sockaddr *) &snl, &slen );
648        if( 0 > res )
649        {
650            if( EAGAIN != errno )
651            {
652                tr_err( "failed to read from routing socket (%s)",
653                        strerror( errno ) );
654            }
655            free( buf );
656            *buflen = 0;
657            return NULL;
658        }
659        if( slen < sizeof( snl ) || AF_NETLINK != snl.nl_family )
660        {
661            tr_err( "bad address" );
662            free( buf );
663            *buflen = 0;
664            return NULL;
665        }
666
667        if( 0 == snl.nl_pid )
668        {
669            break;
670        }
671    }
672
673    *buflen = res;
674
675    return buf;
676}
677
678static int
679parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr )
680{
681    struct nlmsghdr * nlm;
682    struct nlmsgerr * nle;
683    struct rtmsg    * rtm;
684    struct rtattr   * rta;
685    int               rtalen;
686    struct in_addr    gw, dst;
687
688    nlm = ( struct nlmsghdr * ) buf;
689    while( NLMSG_OK( nlm, len ) )
690    {
691        gw.s_addr = INADDR_ANY;
692        dst.s_addr = INADDR_ANY;
693        if( NLMSG_ERROR == nlm->nlmsg_type )
694        {
695            nle = (struct nlmsgerr *) NLMSG_DATA( nlm );
696            if( NLMSG_LENGTH( NLMSG_ALIGN( sizeof( struct nlmsgerr ) ) ) >
697                nlm->nlmsg_len )
698            {
699                tr_err( "truncated netlink error" );
700            }
701            else
702            {
703                tr_err( "netlink error (%s)", strerror( nle->error ) );
704            }
705            return 1;
706        }
707        else if( RTM_NEWROUTE == nlm->nlmsg_type && SEQNUM == nlm->nlmsg_seq &&
708                 getpid() == (pid_t) nlm->nlmsg_pid &&
709                 NLMSG_LENGTH( sizeof( struct rtmsg ) ) <= nlm->nlmsg_len )
710        {
711            rtm = NLMSG_DATA( nlm );
712            rta = RTM_RTA( rtm );
713            rtalen = RTM_PAYLOAD( nlm );
714
715            while( RTA_OK( rta, rtalen ) )
716            {
717                if( sizeof( struct in_addr ) <= RTA_PAYLOAD( rta ) )
718                {
719                    switch( rta->rta_type )
720                    {
721                        case RTA_GATEWAY:
722                            memcpy( &gw, RTA_DATA( rta ), sizeof( gw ) );
723                            break;
724                        case RTA_DST:
725                            memcpy( &dst, RTA_DATA( rta ), sizeof( dst ) );
726                            break;
727                    }
728                }
729                rta = RTA_NEXT( rta, rtalen );
730            }
731        }
732
733        if( INADDR_NONE != gw.s_addr && INADDR_ANY != gw.s_addr &&
734            INADDR_ANY == dst.s_addr )
735        {
736            *addr = gw;
737            return 0;
738        }
739
740        nlm = NLMSG_NEXT( nlm, len );
741    }
742
743    return 1;
744}
745
746#else /* not BSD or Linux */
747
748int
749tr_getDefaultRoute( struct in_addr * addr UNUSED )
750{
751    tr_inf( "don't know how to get default route on this platform" );
752    return 1;
753}
754
755#endif
756
757/***
758****
759***/
760
761static void
762tr_rwSignal( tr_rwlock_t * rw )
763{
764  if ( rw->wantToWrite )
765    tr_condSignal( &rw->writeCond );
766  else if ( rw->wantToRead )
767    tr_condBroadcast( &rw->readCond );
768}
769
770void
771tr_rwInit ( tr_rwlock_t * rw )
772{
773    memset( rw, 0, sizeof(tr_rwlock_t) );
774    tr_lockInit( &rw->lock );
775    tr_condInit( &rw->readCond );
776    tr_condInit( &rw->writeCond );
777}
778
779void
780tr_rwReaderLock( tr_rwlock_t * rw )
781{
782    tr_lockLock( &rw->lock );
783    rw->wantToRead++;
784    while( rw->haveWriter || rw->wantToWrite )
785        tr_condWait( &rw->readCond, &rw->lock );
786    rw->wantToRead--;
787    rw->readCount++;
788    tr_lockUnlock( &rw->lock );
789}
790
791int
792tr_rwReaderTrylock( tr_rwlock_t * rw )
793{
794    int ret = FALSE;
795    tr_lockLock( &rw->lock );
796    if ( !rw->haveWriter && !rw->wantToWrite ) {
797        rw->readCount++;
798        ret = TRUE;
799    }
800    tr_lockUnlock( &rw->lock );
801    return ret;
802
803}
804
805void
806tr_rwReaderUnlock( tr_rwlock_t * rw )
807{
808    tr_lockLock( &rw->lock );
809    --rw->readCount;
810    if( !rw->readCount )
811        tr_rwSignal( rw );
812    tr_lockUnlock( &rw->lock );
813}
814
815void
816tr_rwWriterLock( tr_rwlock_t * rw )
817{
818    tr_lockLock( &rw->lock );
819    rw->wantToWrite++;
820    while( rw->haveWriter || rw->readCount )
821        tr_condWait( &rw->writeCond, &rw->lock );
822    rw->wantToWrite--;
823    rw->haveWriter = TRUE;
824    tr_lockUnlock( &rw->lock );
825}
826
827int
828tr_rwWriterTrylock( tr_rwlock_t * rw )
829{
830    int ret = FALSE;
831    tr_lockLock( &rw->lock );
832    if( !rw->haveWriter && !rw->readCount )
833        ret = rw->haveWriter = TRUE;
834    tr_lockUnlock( &rw->lock );
835    return ret;
836}
837void
838tr_rwWriterUnlock( tr_rwlock_t * rw )
839{
840    tr_lockLock( &rw->lock );
841    rw->haveWriter = FALSE;
842    tr_rwSignal( rw );
843    tr_lockUnlock( &rw->lock );
844}
845
846void
847tr_rwClose( tr_rwlock_t * rw )
848{
849    tr_condClose( &rw->writeCond );
850    tr_condClose( &rw->readCond );
851    tr_lockClose( &rw->lock );
852}
Note: See TracBrowser for help on using the repository browser.