source: trunk/libtransmission/platform.c @ 2391

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

Include trcompat.h for asprintf()
Don't mix function and non function pointers without casting.
Replace a couple bzero()s with memset()s.
Remove a stray ;
Include a missing header.

  • Property svn:keywords set to Date Rev Author Id
File size: 19.1 KB
Line 
1/******************************************************************************
2 * $Id: platform.c 2391 2007-07-18 17:25:42Z 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 * (*) (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.