mapthread.c 11.3 KB
Newer Older
1
/******************************************************************************
2
 * $Id$
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * Project:  UMN MapServer
 * Purpose:  Support code for abstracting thread issues.
 * Author:   Frank Warmerdam, warmerdam@pobox.com
 *
 ******************************************************************************
 * Copyright (c) 1996-2005 Regents of the University of Minnesota.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
18
 * The above copyright notice and this permission notice shall be included in
19 20 21 22 23 24 25 26 27 28 29 30
 * all copies of this Software or works derived from this Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 ****************************************************************************/

/******************************************************************************
31

32 33 34 35 36 37 38 39
            THREAD-SAFE SUPPORT IN MAPSERVER
            ================================

If thread safety is enabled the USE_THREAD macro will be defined.

Thread API (mapthread.h/c)
--------------------------

40
This API is made available to avoid having dependencies on different
41 42
thread libraries in lots of places in MapServer.  It is intended to provide
minimal services required by mapserver and isn't intended to be broadly useful.
43
It should be available for Win32 and pthreads environments.  It should be
44 45
possible to implement for other thread libraries if needed.

46 47 48 49
  int msGetThreadId():
    Returns the current threads integer id.  This can be used for making
        some information thread specific, as has been done for the global
        error context in maperror.c.
50

51
  void msAcquireLock(int):
52 53 54 55
        Acquires the indicated Mutex.  If it is already held by another thread
        then this thread will block till the other thread releases it.  If
        this thread already holds the mutex then behaviour is undefined.  If
        the mutex id is not valid (not in the range 0 to TLOCK_STATIC_MAX)
56
        then results are undefined.
57 58

  void msReleaseLock(int):
59
        Releases the indicated mutex.  If the lock id is invalid, or if the
60 61 62 63
        mutex is not currently held by this thread then results are undefined.

It is incredibly important to ensure that any mutex that is acquired is
released as soon as possible.  Any flow of control that could result in a
64
mutex not being release is going to be a disaster.
65 66 67

The mutex numbers are defined in mapthread.h with the TLOCK_* codes.  If you
need a new mutex, add a #define in mapthread.h for it.  Currently there is
68
no "dynamic" mutex allocation, but this could be added.
69 70 71 72 73 74


Making Things Thread-safe
-------------------------

Generally, to make MapServer thread-safe it is necessary to ensure that
75 76
different threads aren't read and updating common datastructures at the same
time and that all appropriate state be kept thread specific.
77 78 79

Generally this will mean:
  o The previously global error status (errorObj ms_error) is now thread
80
    specific.  Use msGetErrorObj() to get the current threads error state.
81 82

  o Use of subcomponents that are not thread safe need to be protected by
83
    a Mutex (lock).
84

85 86 87
Currently a mutex is used for the entire map file parsing operation
(msLoadMap() in mapfile.c) since the yacc parser uses a number of global
variables.
88

89 90
It is also done with pj_init() from PROJ.4 since this does not appear to be
thread safe.  It isn't yet clear if pj_transform() is thread safe.
91 92 93

It is expected that mutexes will need to be employed in a variety of other
places to ensure serialized access to risky functionality.  This may apply
94
to sublibraries like GDAL for instance.
95 96 97

If a new section that is not thread-safe is identified (and assuming it
can't be internally modified to make it thread-safe easily), it is necessary
98 99
to define a new mutex (#define a TLOCK code in mapthread.h), and then
surround the resource with acquire and release lock calls.
100

101
eg.
102 103 104
    msAcquireLock( TLOCK_PROJ );
    if( !(p->proj = pj_init(p->numargs, p->args)) ) {
        msReleaseLock( TLOCK_PROJ );
105 106
        msSetError(MS_PROJERR, pj_strerrno(pj_errno),
                   "msProcessProjection()");
107 108
        return(-1);
    }
109

110 111
    msReleaseLock( TLOCK_PROJ );

112 113 114
It is imperative that any acquired locks be released on all possible
control paths or else the MapServer will lock up as other thread try to
acquire the lock and block forever.
115 116 117 118 119 120 121 122 123


Other Thread-safe Issues
------------------------

Some issues are not easily corrected with Mutexes or other similar
mechanisms.  The following restrictions currently apply to MapServer when
trying to use it in thread-safe mode.  Note that failure to adhere to these
constraints will not usually generate nice error messages, instead operation
124
will just fail sometimes.
125 126

1) It is currently assumed that a mapObj belongs only to one thread at a time.
127
That is, there is no effort to syncronize access to a mapObj itself.
128 129

2) Stuff that results in a chdir() call are problematic.  In particular, the
130
.map file SHAPEPATH directive should not be used.  Use full paths to data
131 132 133 134 135 136 137 138 139
files instead.

******************************************************************************/


#include <assert.h>
#include "mapserver.h"
#include "mapthread.h"

140

141 142 143 144

#if defined(USE_THREAD)
static int thread_debug = 0;

145 146
static char *lock_names[] = {
  NULL, "PARSER", "GDAL", "ERROROBJ", "PROJ", "TTF", "POOL", "SDE",
147
  "ORACLE", "OWS", "LAYER_VTABLE", "IOCONTEXT", "TMPFILE", "DEBUGOBJ", "OGR", "TIME", "FRIBIDI", "WXS", "GEOS", NULL
148
};
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
#endif

/************************************************************************/
/* ==================================================================== */
/*                               PTHREADS                               */
/* ==================================================================== */
/************************************************************************/

#if defined(USE_THREAD) && !defined(_WIN32)

#include "pthread.h"

static int mutexes_initialized = 0;
static pthread_mutex_t mutex_locks[TLOCK_MAX];

/************************************************************************/
/*                            msThreadInit()                            */
/************************************************************************/

void msThreadInit()

{
171
  static pthread_mutex_t core_lock = PTHREAD_MUTEX_INITIALIZER;
172

173 174
  if( thread_debug )
    fprintf( stderr, "msThreadInit() (posix)\n" );
175

176
  pthread_mutex_lock( &core_lock );
177

178 179
  for( ; mutexes_initialized < TLOCK_STATIC_MAX; mutexes_initialized++ )
    pthread_mutex_init( mutex_locks + mutexes_initialized, NULL );
180

181
  pthread_mutex_unlock( &core_lock );
182 183 184 185 186 187
}

/************************************************************************/
/*                           msGetThreadId()                            */
/************************************************************************/

188
void* msGetThreadId()
189 190

{
191
  return (void*) pthread_self();
192 193 194 195 196 197 198 199 200
}

/************************************************************************/
/*                           msAcquireLock()                            */
/************************************************************************/

void msAcquireLock( int nLockId )

{
201 202
  if( mutexes_initialized == 0 )
    msThreadInit();
203

204
  assert( nLockId >= 0 && nLockId < mutexes_initialized );
205

206 207 208
  if( thread_debug )
    fprintf( stderr, "msAcquireLock(%d/%s) (posix)\n",
             nLockId, lock_names[nLockId] );
209

210
  pthread_mutex_lock( mutex_locks + nLockId );
211 212 213 214 215 216 217 218 219
}

/************************************************************************/
/*                           msReleaseLock()                            */
/************************************************************************/

void msReleaseLock( int nLockId )

{
220 221
  assert( mutexes_initialized > 0 );
  assert( nLockId >= 0 && nLockId < mutexes_initialized );
222

223 224 225
  if( thread_debug )
    fprintf( stderr, "msReleaseLock(%d/%s) (posix)\n",
             nLockId, lock_names[nLockId] );
226

227
  pthread_mutex_unlock( mutex_locks + nLockId );
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
}

#endif /* defined(USE_THREAD) && !defined(_WIN32) */

/************************************************************************/
/* ==================================================================== */
/*                          WIN32 THREADS                               */
/* ==================================================================== */
/************************************************************************/

#if defined(USE_THREAD) && defined(_WIN32)

#include <windows.h>

static int mutexes_initialized = 0;
static HANDLE mutex_locks[TLOCK_MAX];

/************************************************************************/
/*                            msThreadInit()                            */
/************************************************************************/

void msThreadInit()

{
252 253
  /* static pthread_mutex_t core_lock = PTHREAD_MUTEX_INITIALIZER; */
  static HANDLE core_lock = NULL;
254

255 256
  if( mutexes_initialized >= TLOCK_STATIC_MAX )
    return;
257

258 259
  if( thread_debug )
    fprintf( stderr, "msThreadInit() (win32)\n" );
260

261 262 263 264
  if( core_lock == NULL )
    core_lock = CreateMutex( NULL, TRUE, NULL );
  else
    WaitForSingleObject( core_lock, INFINITE );
265

266 267
  for( ; mutexes_initialized < TLOCK_STATIC_MAX; mutexes_initialized++ )
    mutex_locks[mutexes_initialized] = CreateMutex( NULL, FALSE, NULL );
268

269
  ReleaseMutex( core_lock );
270 271 272 273 274 275
}

/************************************************************************/
/*                           msGetThreadId()                            */
/************************************************************************/

276
void* msGetThreadId()
277 278

{
279
  return GetCurrentThreadId();
280 281 282 283 284 285 286 287 288
}

/************************************************************************/
/*                           msAcquireLock()                            */
/************************************************************************/

void msAcquireLock( int nLockId )

{
289 290
  if( mutexes_initialized == 0 )
    msThreadInit();
291

292
  assert( nLockId >= 0 && nLockId < mutexes_initialized );
293

294 295 296
  if( thread_debug )
    fprintf( stderr, "msAcquireLock(%d/%s) (win32)\n",
             nLockId, lock_names[nLockId] );
297

298
  WaitForSingleObject( mutex_locks[nLockId], INFINITE );
299 300 301 302 303 304 305 306 307
}

/************************************************************************/
/*                           msReleaseLock()                            */
/************************************************************************/

void msReleaseLock( int nLockId )

{
308 309
  assert( mutexes_initialized > 0 );
  assert( nLockId >= 0 && nLockId < mutexes_initialized );
310

311 312 313
  if( thread_debug )
    fprintf( stderr, "msReleaseLock(%d/%s) (win32)\n",
             nLockId, lock_names[nLockId] );
314

315
  ReleaseMutex( mutex_locks[nLockId] );
316 317 318
}

#endif /* defined(USE_THREAD) && defined(_WIN32) */