source: trunk/libtransmission/platform.c @ 7975

Last change on this file since 7975 was 7975, checked in by livings124, 13 years ago

#1710 the daemon will use the Application Support folder on Mac

  • Property svn:keywords set to Date Rev Author Id
File size: 17.5 KB
RevLine 
[6389]1/*
[7658]2 * This file Copyright (C) 2008-2009 Charles Kerr <charles@transmissionbt.com>
[14]3 *
[6389]4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
[6795]6 * so that the bulk of its code can remain under the MIT license.
[6389]7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
[260]9 *
[6389]10 * $Id: platform.c 7975 2009-02-28 21:45:16Z livings124 $
11 */
[14]12
[7232]13#ifdef WIN32
[6795]14 #include <windows.h>
[6892]15 #include <shlobj.h> /* for CSIDL_APPDATA, CSIDL_MYDOCUMENTS */
[2552]16#else
[6795]17 #ifdef SYS_DARWIN
18  #include <CoreFoundation/CoreFoundation.h>
19 #endif
20
21 #define _XOPEN_SOURCE 500  /* needed for recursive locks. */
22 #ifndef __USE_UNIX98
[4041]23  #define __USE_UNIX98 /* some older Linuxes need it spelt out for them */
[6795]24 #endif
25 #include <pthread.h>
[20]26#endif
[2552]27
[7407]28#include <assert.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32
[6320]33#include <sys/stat.h>
[310]34#include <sys/types.h>
[6892]35#ifdef WIN32
36#include <libgen.h>
37#endif
[310]38#include <dirent.h>
[4001]39#include <fcntl.h>
40#include <unistd.h> /* getuid getpid close */
[2544]41
[20]42#include "transmission.h"
[7476]43#include "session.h"
[6319]44#include "list.h"
[2573]45#include "platform.h"
[2552]46#include "utils.h"
[20]47
[2552]48/***
49****  THREADS
50***/
51
[7232]52#ifdef WIN32
[3254]53typedef DWORD tr_thread_id;
54#else
55typedef pthread_t tr_thread_id;
56#endif
57
58static tr_thread_id
59tr_getCurrentThread( void )
60{
[7232]61#ifdef WIN32
[6795]62    return GetCurrentThreadId( );
[3254]63#else
64    return pthread_self( );
65#endif
66}
67
68static int
[6795]69tr_areThreadsEqual( tr_thread_id a,
70                    tr_thread_id b )
[3254]71{
[7232]72#ifdef WIN32
[3254]73    return a == b;
74#else
75    return pthread_equal( a, b );
76#endif
77}
78
[3105]79struct tr_thread
[2552]80{
[6795]81    void            ( * func )( void * );
82    void *          arg;
83    tr_thread_id    thread;
[5164]84#ifdef WIN32
[6795]85    HANDLE          thread_handle;
[2552]86#endif
87};
88
[3254]89int
[6795]90tr_amInThread( const tr_thread * t )
[3254]91{
[6795]92    return tr_areThreadsEqual( tr_getCurrentThread( ), t->thread );
[3254]93}
94
[2577]95#ifdef WIN32
[6795]96 #define ThreadFuncReturnType unsigned WINAPI
[2577]97#else
[6795]98 #define ThreadFuncReturnType void
[2577]99#endif
100
101static ThreadFuncReturnType
[2552]102ThreadFunc( void * _t )
103{
[3105]104    tr_thread * t = _t;
[2552]105
106    t->func( t->arg );
[2577]107
108#ifdef WIN32
109    _endthreadex( 0 );
110    return 0;
111#endif
[2552]112}
113
[3105]114tr_thread *
[6795]115tr_threadNew( void   ( *func )(void *),
116              void * arg )
[2552]117{
[3105]118    tr_thread * t = tr_new0( tr_thread, 1 );
[6795]119
[5171]120    t->func = func;
121    t->arg  = arg;
[2552]122
[7232]123#ifdef WIN32
[5171]124    {
125        unsigned int id;
[6795]126        t->thread_handle =
127            (HANDLE) _beginthreadex( NULL, 0, &ThreadFunc, t, 0,
128                                     &id );
[5171]129        t->thread = (DWORD) id;
130    }
[2552]131#else
[6795]132    pthread_create( &t->thread, NULL, ( void * ( * )(
133                                           void * ) )ThreadFunc, t );
[2552]134#endif
135
136    return t;
137}
[6795]138
[2552]139/***
140****  LOCKS
141***/
142
[3105]143struct tr_lock
[2552]144{
[6795]145    int                 depth;
[7232]146#ifdef WIN32
[6795]147    CRITICAL_SECTION    lock;
148    DWORD               lockThread;
[2552]149#else
[6795]150    pthread_mutex_t     lock;
151    pthread_t           lockThread;
[2552]152#endif
153};
154
[3105]155tr_lock*
[2552]156tr_lockNew( void )
157{
[6795]158    tr_lock *           l = tr_new0( tr_lock, 1 );
[2552]159
[7232]160#ifdef WIN32
[5645]161    InitializeCriticalSection( &l->lock ); /* supports recursion */
[2552]162#else
[4040]163    pthread_mutexattr_t attr;
164    pthread_mutexattr_init( &attr );
165    pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
166    pthread_mutex_init( &l->lock, &attr );
[2552]167#endif
168
169    return l;
170}
171
172void
[3105]173tr_lockFree( tr_lock * l )
[2552]174{
[7232]175#ifdef WIN32
[2577]176    DeleteCriticalSection( &l->lock );
[2552]177#else
178    pthread_mutex_destroy( &l->lock );
179#endif
180    tr_free( l );
181}
182
183void
[3105]184tr_lockLock( tr_lock * l )
[2552]185{
[7232]186#ifdef WIN32
[4040]187    EnterCriticalSection( &l->lock );
[2552]188#else
[4040]189    pthread_mutex_lock( &l->lock );
[2552]190#endif
[4043]191    assert( l->depth >= 0 );
192    if( l->depth )
[6795]193        assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
[4042]194    l->lockThread = tr_getCurrentThread( );
[4040]195    ++l->depth;
[2552]196}
197
[3254]198int
199tr_lockHave( const tr_lock * l )
200{
201    return ( l->depth > 0 )
[6795]202           && ( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
[3254]203}
204
[2552]205void
[3105]206tr_lockUnlock( tr_lock * l )
[2552]207{
[4041]208    assert( l->depth > 0 );
[6795]209    assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
[3254]210
[4042]211    --l->depth;
212    assert( l->depth >= 0 );
[7232]213#ifdef WIN32
[4040]214    LeaveCriticalSection( &l->lock );
[2552]215#else
[4040]216    pthread_mutex_unlock( &l->lock );
[2552]217#endif
218}
219
220/***
221****  PATHS
222***/
223
[7232]224#ifndef WIN32
[6795]225 #include <pwd.h>
[2591]226#endif
[1000]227
[4179]228static const char *
[5517]229getHomeDir( void )
[1000]230{
[5517]231    static char * home = NULL;
[1000]232
[5517]233    if( !home )
234    {
235        home = tr_strdup( getenv( "HOME" ) );
[1000]236
[5517]237        if( !home )
238        {
[2591]239#ifdef WIN32
[7019]240            char appdata[MAX_PATH]; /* SHGetFolderPath() requires MAX_PATH */
241            *appdata = '\0';
[7663]242            SHGetFolderPath( NULL, CSIDL_PERSONAL, NULL, 0, appdata );
[7019]243            home = tr_strdup( appdata );
[2591]244#else
[6795]245            struct passwd * pw = getpwuid( getuid( ) );
[5517]246            if( pw )
247                home = tr_strdup( pw->pw_dir );
[5645]248            endpwent( );
[2591]249#endif
[310]250        }
251
[5517]252        if( !home )
253            home = tr_strdup( "" );
[310]254    }
[5517]255
256    return home;
[310]257}
258
[5517]259static const char *
260getOldConfigDir( void )
[20]261{
[5517]262    static char * path = NULL;
[20]263
[5517]264    if( !path )
[4179]265    {
[7232]266#ifdef SYS_DARWIN
[6896]267        path = tr_buildPath( getHomeDir( ), "Library",
268                              "Application Support",
269                              "Transmission", NULL );
[6795]270#elif defined( WIN32 )
[7019]271        char appdata[MAX_PATH]; /* SHGetFolderPath() requires MAX_PATH */
[4179]272        SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
[6896]273        path = tr_buildPath( appdata, "Transmission", NULL );
[20]274#else
[6896]275        path = tr_buildPath( getHomeDir( ), ".transmission", NULL );
[20]276#endif
[4179]277    }
[20]278
[5517]279    return path;
280}
[20]281
[6896]282#if defined(SYS_DARWIN) || defined(WIN32)
283 #define RESUME_SUBDIR  "Resume"
284 #define TORRENT_SUBDIR "Torrents"
285#else
286 #define RESUME_SUBDIR  "resume"
287 #define TORRENT_SUBDIR "torrents"
288#endif
289
[5517]290static const char *
291getOldTorrentsDir( void )
292{
293    static char * path = NULL;
294
295    if( !path )
[6896]296        path = tr_buildPath( getOldConfigDir( ), TORRENT_SUBDIR, NULL );
[310]297
[5517]298    return path;
[310]299}
[6795]300
[5517]301static const char *
302getOldCacheDir( void )
[310]303{
[5517]304    static char * path = NULL;
[310]305
[5517]306    if( !path )
307    {
[7232]308#if defined( WIN32 )
[6896]309        path = tr_buildPath( getOldConfigDir( ), "Cache", NULL );
[310]310#elif defined( SYS_DARWIN )
[6896]311        path = tr_buildPath( getHomeDir( ), "Library", "Caches", "Transmission", NULL );
[310]312#else
[6896]313        path = tr_buildPath( getOldConfigDir( ), "cache", NULL );
[310]314#endif
[5517]315    }
[310]316
[5517]317    return path;
318}
319
320static void
[6795]321moveFiles( const char * oldDir,
322           const char * newDir )
[5517]323{
324    if( oldDir && newDir && strcmp( oldDir, newDir ) )
325    {
326        DIR * dirh = opendir( oldDir );
327        if( dirh )
328        {
[6795]329            int             count = 0;
[5517]330            struct dirent * dirp;
[6795]331            while( ( dirp = readdir( dirh ) ) )
[5517]332            {
[6795]333                if( strcmp( dirp->d_name,
334                            "." ) && strcmp( dirp->d_name, ".." ) )
[5517]335                {
[6896]336                    char * o = tr_buildPath( oldDir, dirp->d_name, NULL );
337                    char * n = tr_buildPath( newDir, dirp->d_name, NULL );
[5517]338                    rename( o, n );
339                    ++count;
[6896]340                    tr_free( n );
341                    tr_free( o );
[5517]342                }
343            }
[5576]344
345            if( count )
346                tr_inf( _( "Migrated %1$d files from \"%2$s\" to \"%3$s\"" ),
347                        count, oldDir, newDir );
[5517]348            closedir( dirh );
349        }
350    }
351}
352
353static void
[7385]354migrateFiles( const tr_session * session )
[5517]355{
356    static int migrated = FALSE;
357
358    if( !migrated )
359    {
360        const char * oldDir;
361        const char * newDir;
362        migrated = TRUE;
363
364        oldDir = getOldTorrentsDir( );
[7385]365        newDir = tr_getTorrentDir( session );
[5517]366        moveFiles( oldDir, newDir );
367
368        oldDir = getOldCacheDir( );
[7385]369        newDir = tr_getResumeDir( session );
[5517]370        moveFiles( oldDir, newDir );
371    }
372}
373
374void
[7385]375tr_setConfigDir( tr_session * session,
[6795]376                 const char * configDir )
[5517]377{
[6896]378    char * path;
[5517]379
[7385]380    session->configDir = tr_strdup( configDir );
[5517]381
[6896]382    path = tr_buildPath( configDir, RESUME_SUBDIR, NULL );
383    tr_mkdirp( path, 0777 );
[7385]384    session->resumeDir = path;
[310]385
[6896]386    path = tr_buildPath( configDir, TORRENT_SUBDIR, NULL );
387    tr_mkdirp( path, 0777 );
[7385]388    session->torrentDir = path;
[310]389
[7385]390    migrateFiles( session );
[310]391}
392
[2154]393const char *
[7385]394tr_sessionGetConfigDir( const tr_session * session )
[310]395{
[7385]396    return session->configDir;
[5517]397}
[310]398
[5517]399const char *
[7385]400tr_getTorrentDir( const tr_session * session )
[5517]401{
[7385]402    return session->torrentDir;
[5517]403}
[2154]404
[5517]405const char *
[7385]406tr_getResumeDir( const tr_session * session )
[5517]407{
[7385]408    return session->resumeDir;
[5517]409}
410
411const char*
[7367]412tr_getDefaultConfigDir( const char * appname )
[5517]413{
414    static char * s = NULL;
415
[7367]416    if( !appname || !*appname )
417        appname = "Transmission";
418
[5517]419    if( !s )
420    {
[6795]421        if( ( s = getenv( "TRANSMISSION_HOME" ) ) )
[5517]422        {
[6896]423            s = tr_strdup( s );
[5517]424        }
425        else
426        {
[5519]427#ifdef SYS_DARWIN
[7367]428            s = tr_buildPath( getHomeDir( ), "Library", "Application Support",
429                              appname, NULL );
[6795]430#elif defined( WIN32 )
[6937]431            char appdata[MAX_PATH]; /* SHGetFolderPath() requires MAX_PATH */
432            SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
[7367]433            s = tr_buildPath( appdata, appname, NULL );
[310]434#else
[6795]435            if( ( s = getenv( "XDG_CONFIG_HOME" ) ) )
[7367]436                s = tr_buildPath( s, appname, NULL );
[5517]437            else
[7367]438                s = tr_buildPath( getHomeDir( ), ".config", appname, NULL );
[20]439#endif
[5517]440        }
441    }
442
443    return s;
[14]444}
[5167]445
[7113]446const char*
447tr_getDefaultDownloadDir( void )
448{
449    static char * s = NULL;
450
451    if( s == NULL )
452        s = tr_buildPath( getHomeDir( ), "Downloads", NULL );
453
454    return s;
455}
456
[5167]457/***
458****
459***/
460
[6319]461static int
462isClutchDir( const char * path )
463{
464    struct stat sb;
[6896]465    char * tmp = tr_buildPath( path, "javascript", "transmission.js", NULL );
466    const int ret = !stat( tmp, &sb );
[6323]467    tr_inf( _( "Searching for web interface file \"%s\"" ), tmp );
[6896]468    tr_free( tmp );
469    return ret;
470   
[6319]471}
472
473const char *
474tr_getClutchDir( const tr_session * session UNUSED )
475{
476    static char * s = NULL;
477
478    if( !s )
479    {
[6795]480        if( ( s = getenv( "CLUTCH_HOME" ) ) )
[6319]481        {
[6896]482            s = tr_strdup( s );
[6319]483        }
[6795]484        else if( ( s = getenv( "TRANSMISSION_WEB_HOME" ) ) )
[6328]485        {
[6896]486            s = tr_strdup( s );
[6328]487        }
[6319]488        else
489        {
[6924]490
[7975]491#ifdef SYS_DARWIN /* on Mac, look in the app package first, before default unix directories */
[6924]492
493            CFURLRef appURL = CFBundleCopyBundleURL( CFBundleGetMainBundle( ) );
494            CFStringRef appRef = CFURLCopyFileSystemPath( appURL,
495                                                         kCFURLPOSIXPathStyle );
496            const char * appString = CFStringGetCStringPtr( appRef,
497                                         CFStringGetFastestEncoding( appRef ) );
[6642]498            CFRelease( appURL );
499            CFRelease( appRef );
[6321]500
[6896]501            s = tr_buildPath( appString, "Contents", "Resources", "web", NULL );
[7975]502           
503            if( !isClutchDir( s ) ) {
504                tr_free( s );
505               
506                /* Fallback to the Application Support folder */
507                s = tr_buildPath( tr_sessionGetConfigDir( session ), "web", NULL );
508                if( !isClutchDir( s ) ) {
509                    tr_free( s );
510                    s = NULL;
511                }
512            }
[6924]513
[6795]514#elif defined( WIN32 )
515
[6924]516            /* SHGetFolderPath explicitly requires MAX_PATH length */
517            char dir[MAX_PATH];
518           
519            /* Generally, Web interface should be stored in a Web subdir of
520             * calling executable dir. */
521
522            if( s == NULL ) { 
523                /* First, we should check personal AppData/Transmission/Web */
524                SHGetFolderPath( NULL, CSIDL_COMMON_APPDATA, NULL, 0, dir );
525                s = tr_buildPath( dir, "Transmission", "Web", NULL );
526                if( !isClutchDir( s ) ) {
527                    tr_free( s );
528                    s = NULL;
529                }
530            }
531
532            if( s == NULL ) {
533                /* check personal AppData */
534                SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, dir );
535                s = tr_buildPath( dir, "Transmission", "Web", NULL );
536                if( !isClutchDir( s ) ) {
537                    tr_free( s );
538                    s = NULL;
539                }
540            }
541
542            if( s == NULL) {
543                /* check calling module place */
544                GetModuleFileName( GetModuleHandle( NULL ), dir, sizeof( dir ) );
545                s = tr_buildPath( dirname( dir ), "Web", NULL );
546                if( !isClutchDir( s ) ) {
547                    tr_free( s );
548                    s = NULL;
549                }
550            }
551
[7975]552#else /* follow the XDG spec */
[6924]553
[6795]554            tr_list *candidates = NULL, *l;
[6992]555            const char * tmp;
[6319]556
557            /* XDG_DATA_HOME should be the first in the list of candidates */
[6992]558            tmp = getenv( "XDG_DATA_HOME" );
559            if( tmp && *tmp )
560                tr_list_append( &candidates, tr_strdup( tmp ) );
[6896]561            else {
562                char * dhome = tr_buildPath( getHomeDir( ), ".local", "share", NULL );
563                tr_list_append( &candidates, dhome );
[6319]564            }
565
566            /* XDG_DATA_DIRS are the backup directories */
[7373]567            {
[7405]568                const char * pkg = PACKAGE_DATA_DIR;
569                const char * xdg = getenv( "XDG_DATA_DIRS" );
570                const char * fallback = "/usr/local/share:/usr/share";
571                char * buf = tr_strdup_printf( "%s:%s:%s", (pkg?pkg:""), (xdg?xdg:""), fallback );
572                tmp = buf;
[7373]573                while( tmp && *tmp ) {
574                    const char * end = strchr( tmp, ':' );
575                    if( end ) {
[7405]576                        if( ( end - tmp ) > 1 )
577                            tr_list_append( &candidates, tr_strndup( tmp, end - tmp ) );
[7373]578                        tmp = end + 1;
579                    } else if( tmp && *tmp ) {
580                        tr_list_append( &candidates, tr_strdup( tmp ) );
581                        break;
582                    }
[6319]583                }
[7405]584                tr_free( buf );
[6319]585            }
586
[6992]587            /* walk through the candidates & look for a match */
588            for( l=candidates; l; l=l->next ) {
[6896]589                char * path = tr_buildPath( l->data, "transmission", "web", NULL );
590                const int found = isClutchDir( path );
591                if( found ) {
592                    s = path;
[6319]593                    break;
[6896]594                }
[6901]595                tr_free( path );
[6319]596            }
597
598            tr_list_free( &candidates, tr_free );
[6924]599
[6319]600#endif
[6924]601
[6319]602        }
603    }
604
605    return s;
606}
607
608/***
609****
610***/
611
[6559]612tr_lockfile_state_t
[5167]613tr_lockfile( const char * filename )
614{
[6559]615    tr_lockfile_state_t ret;
[5167]616
617#ifdef WIN32
618
[6795]619    HANDLE              file = CreateFile(
620        filename,
621        GENERIC_READ | GENERIC_WRITE,
622        FILE_SHARE_READ |
623        FILE_SHARE_WRITE,
624        NULL,
625        OPEN_ALWAYS,
626        FILE_ATTRIBUTE_NORMAL,
627        NULL );
[5167]628    if( file == INVALID_HANDLE_VALUE )
629        ret = TR_LOCKFILE_EOPEN;
630    else if( !LockFile( file, 0, 0, 1, 1 ) )
631        ret = TR_LOCKFILE_ELOCK;
632    else
633        ret = TR_LOCKFILE_SUCCESS;
634
635#else
636
637    int fd = open( filename, O_RDWR | O_CREAT, 0666 );
638    if( fd < 0 )
639        ret = TR_LOCKFILE_EOPEN;
[6795]640    else
641    {
[5167]642        struct flock lk;
643        memset( &lk, 0,  sizeof( lk ) );
644        lk.l_start = 0;
645        lk.l_len = 0;
646        lk.l_type = F_WRLCK;
647        lk.l_whence = SEEK_SET;
648        if( -1 == fcntl( fd, F_SETLK, &lk ) )
649            ret = TR_LOCKFILE_ELOCK;
650        else
651            ret = TR_LOCKFILE_SUCCESS;
652    }
653
654#endif
655
656    return ret;
657}
[6702]658
659#ifdef WIN32
660
661/* The following mmap functions are by Joerg Walter, and were taken from
662 * his paper at: http://www.genesys-e.de/jwalter/mix4win.htm
663 */
664
[6902]665#if defined(_MSC_VER)
666__declspec( align( 4 ) ) static LONG volatile g_sl;
667#else
[6795]668static LONG volatile g_sl __attribute__ ( ( aligned ( 4 ) ) );
[6902]669#endif
[6710]670
[6702]671/* Wait for spin lock */
[6795]672static int
673slwait( LONG volatile *sl )
674{
675    while( InterlockedCompareExchange ( sl, 1, 0 ) != 0 )
676        Sleep ( 0 );
677
[6702]678    return 0;
679}
680
681/* Release spin lock */
[6795]682static int
[6902]683slrelease( LONG volatile *sl )
[6795]684{
685    InterlockedExchange ( sl, 0 );
[6702]686    return 0;
687}
688
[6711]689/* getpagesize for windows */
[6795]690static long
691getpagesize( void )
692{
[6711]693    static long g_pagesize = 0;
[6795]694
695    if( !g_pagesize )
696    {
[6711]697        SYSTEM_INFO system_info;
[6795]698        GetSystemInfo ( &system_info );
[6711]699        g_pagesize = system_info.dwPageSize;
700    }
701    return g_pagesize;
702}
703
[6795]704static long
705getregionsize( void )
706{
[6711]707    static long g_regionsize = 0;
[6795]708
709    if( !g_regionsize )
710    {
[6711]711        SYSTEM_INFO system_info;
[6795]712        GetSystemInfo ( &system_info );
[6711]713        g_regionsize = system_info.dwAllocationGranularity;
714    }
715    return g_regionsize;
716}
717
[6795]718void *
719mmap( void *ptr,
720      long  size,
721      long  prot,
722      long  type,
723      long  handle,
724      long  arg )
725{
[6702]726    static long g_pagesize;
727    static long g_regionsize;
[6795]728
[6702]729    /* Wait for spin lock */
[6795]730    slwait ( &g_sl );
[6702]731    /* First time initialization */
[6795]732    if( !g_pagesize )
733        g_pagesize = getpagesize ( );
734    if( !g_regionsize )
735        g_regionsize = getregionsize ( );
[6702]736    /* Allocate this */
[6795]737    ptr = VirtualAlloc ( ptr, size,
738                         MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
739                         PAGE_READWRITE );
740    if( !ptr )
741    {
742        ptr = (void *) -1;
[6702]743        goto mmap_exit;
744    }
745mmap_exit:
746    /* Release spin lock */
[6795]747    slrelease ( &g_sl );
[6702]748    return ptr;
749}
750
[6795]751long
752munmap( void *ptr,
753        long  size )
754{
[6702]755    static long g_pagesize;
756    static long g_regionsize;
[6795]757    int         rc = -1;
758
[6702]759    /* Wait for spin lock */
[6795]760    slwait ( &g_sl );
[6702]761    /* First time initialization */
[6795]762    if( !g_pagesize )
763        g_pagesize = getpagesize ( );
764    if( !g_regionsize )
765        g_regionsize = getregionsize ( );
[6702]766    /* Free this */
[6795]767    if( !VirtualFree ( ptr, 0,
768                       MEM_RELEASE ) )
[6702]769        goto munmap_exit;
770    rc = 0;
771munmap_exit:
772    /* Release spin lock */
[6795]773    slrelease ( &g_sl );
[6702]774    return rc;
775}
776
777#endif
778
Note: See TracBrowser for help on using the repository browser.