8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-31 04:03:03 +01:00
firebird-mirror/src/common/ThreadStart.cpp
2014-08-20 10:07:19 +00:00

389 lines
9.1 KiB
C++

/*
* PROGRAM: JRD Access Method
* MODULE: ThreadData.cpp
* DESCRIPTION: Thread support routines
*
* The contents of this file are subject to the Interbase Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy
* of the License at http://www.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code was created by Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*
* 2002.10.28 Sean Leyne - Completed removal of obsolete "DGUX" port
*
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
*
* Alex Peshkov
*/
#include "firebird.h"
#include <stdio.h>
#include <errno.h>
#include "../common/ThreadStart.h"
#include "../yvalve/gds_proto.h"
#include "../common/isc_s_proto.h"
#include "../common/gdsassert.h"
#ifdef WIN_NT
#include <process.h>
#include <windows.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_SYSCALL_H
#include <sys/syscall.h>
#endif
#include "../common/classes/locks.h"
#include "../common/classes/rwlock.h"
#include "../common/classes/Synchronize.h"
namespace
{
// due to same names of parameters for various ThreadData::start(...),
// we may use common macro for various platforms
#define THREAD_ENTRYPOINT threadStart
#define THREAD_ARG static_cast<THREAD_ENTRY_PARAM> (FB_NEW(*getDefaultMemoryPool()) \
ThreadArgs(reinterpret_cast<THREAD_ENTRY_RETURN (THREAD_ENTRY_CALL *) (THREAD_ENTRY_PARAM)>(routine), \
static_cast<THREAD_ENTRY_PARAM>(arg)))
class ThreadArgs
{
public:
typedef THREAD_ENTRY_RETURN (THREAD_ENTRY_CALL *Routine) (THREAD_ENTRY_PARAM);
typedef THREAD_ENTRY_PARAM Arg;
private:
Routine routine;
Arg arg;
public:
ThreadArgs(Routine r, Arg a) : routine(r), arg(a) { }
ThreadArgs(const ThreadArgs& t) : routine(t.routine), arg(t.arg) { }
void run() { routine(arg); }
private:
ThreadArgs& operator=(const ThreadArgs&);
};
#ifdef __cplusplus
extern "C"
#endif
THREAD_ENTRY_DECLARE threadStart(THREAD_ENTRY_PARAM arg)
{
fb_assert(arg);
Firebird::ThreadSync thread("threadStart");
MemoryPool::setContextPool(getDefaultMemoryPool());
ThreadArgs localArgs(*static_cast<ThreadArgs*>(arg));
delete static_cast<ThreadArgs*>(arg);
localArgs.run();
return 0;
}
} // anonymous namespace
#ifdef USE_POSIX_THREADS
#define START_THREAD
void Thread::start(ThreadEntryPoint* routine, void* arg, int priority_arg, Handle* p_handle)
{
/**************************************
*
* t h r e a d _ s t a r t ( P O S I X )
*
**************************************
*
* Functional description
* Start a new thread. Return 0 if successful,
* status if not.
*
**************************************/
pthread_t thread;
pthread_attr_t pattr;
int state;
#if defined (LINUX) || defined (FREEBSD)
if (state = pthread_create(&thread, NULL, THREAD_ENTRYPOINT, THREAD_ARG))
Firebird::system_call_failed::raise("pthread_create", state);
if (!p_handle)
{
if (state = pthread_detach(thread))
Firebird::system_call_failed::raise("pthread_detach", state);
}
#else
state = pthread_attr_init(&pattr);
if (state)
Firebird::system_call_failed::raise("pthread_attr_init", state);
#if defined(_AIX) || defined(DARWIN)
// adjust stack size
// For AIX 32-bit compiled applications, the default stacksize is 96 KB,
// see <pthread.h>. For 64-bit compiled applications, the default stacksize
// is 192 KB. This is too small - see HP-UX note above
// For MaxOS default stack is 512 KB, which is also too small in 2012.
size_t stack_size;
state = pthread_attr_getstacksize(&pattr, &stack_size);
if (state)
Firebird::system_call_failed::raise("pthread_attr_getstacksize");
if (stack_size < 0x400000L)
{
state = pthread_attr_setstacksize(&pattr, 0x400000L);
if (state)
Firebird::system_call_failed::raise("pthread_attr_setstacksize", state);
}
#endif // _AIX
state = pthread_attr_setscope(&pattr, PTHREAD_SCOPE_SYSTEM);
if (state)
Firebird::system_call_failed::raise("pthread_attr_setscope", state);
if (!p_handle)
{
state = pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED);
if (state)
Firebird::system_call_failed::raise("pthread_attr_setdetachstate", state);
}
state = pthread_create(&thread, &pattr, THREAD_ENTRYPOINT, THREAD_ARG);
int state2 = pthread_attr_destroy(&pattr);
if (state)
Firebird::system_call_failed::raise("pthread_create", state);
if (state2)
Firebird::system_call_failed::raise("pthread_attr_destroy", state2);
#endif
if (p_handle)
{
#ifdef HAVE_PTHREAD_CANCEL
int dummy; // We do not want to know old cancel type
state = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &dummy);
if (state)
Firebird::system_call_failed::raise("pthread_setcanceltype", state);
#endif
*p_handle = thread;
}
}
void Thread::waitForCompletion(Handle& thread)
{
int state = pthread_join(thread, NULL);
if (state)
Firebird::system_call_failed::raise("pthread_join", state);
}
void Thread::kill(Handle& thread)
{
#ifdef HAVE_PTHREAD_CANCEL
int state = pthread_cancel(thread);
if (state)
Firebird::system_call_failed::raise("pthread_cancel", state);
waitForCompletion(thread);
#endif
}
ThreadId Thread::getId()
{
#ifdef LINUX
return syscall(SYS_gettid);
#else
return pthread_self();
#endif
}
void Thread::sleep(unsigned milliseconds)
{
#if defined(HAVE_NANOSLEEP)
timespec timer, rem;
timer.tv_sec = milliseconds / 1000;
timer.tv_nsec = (milliseconds % 1000) * 1000000;
while (nanosleep(&timer, &rem) != 0)
{
if (errno != EINTR)
{
Firebird::system_call_failed::raise("nanosleep");
}
timer = rem;
}
#else
Firebird::Semaphore timer;
timer.tryEnter(0, milliseconds);
#endif
}
void Thread::yield()
{
// use sched_yield() instead of pthread_yield(). Because pthread_yield()
// is not part of the (final) POSIX 1003.1c standard. Several drafts of
// the standard contained pthread_yield(), but then the POSIX guys
// discovered it was redundant with sched_yield() and dropped it.
// So, just use sched_yield() instead. POSIX systems on which
// sched_yield() is available define _POSIX_PRIORITY_SCHEDULING
// in <unistd.h>. Darwin defined _POSIX_THREAD_PRIORITY_SCHEDULING
// instead of _POSIX_PRIORITY_SCHEDULING.
#if (defined _POSIX_PRIORITY_SCHEDULING || defined _POSIX_THREAD_PRIORITY_SCHEDULING)
sched_yield();
#else
pthread_yield();
#endif // _POSIX_PRIORITY_SCHEDULING
}
#endif // USE_POSIX_THREADS
#ifdef WIN_NT
#define START_THREAD
void Thread::start(ThreadEntryPoint* routine, void* arg, int priority_arg, Handle* p_handle)
{
/**************************************
*
* t h r e a d _ s t a r t ( W I N _ N T )
*
**************************************
*
* Functional description
* Start a new thread. Return 0 if successful,
* status if not.
*
**************************************/
int priority;
switch (priority_arg)
{
case THREAD_critical:
priority = THREAD_PRIORITY_TIME_CRITICAL;
break;
case THREAD_high:
priority = THREAD_PRIORITY_HIGHEST;
break;
case THREAD_medium_high:
priority = THREAD_PRIORITY_ABOVE_NORMAL;
break;
case THREAD_medium:
priority = THREAD_PRIORITY_NORMAL;
break;
case THREAD_medium_low:
priority = THREAD_PRIORITY_BELOW_NORMAL;
break;
case THREAD_low:
default:
priority = THREAD_PRIORITY_LOWEST;
break;
}
/* I have changed the CreateThread here to _beginthreadex() as using
* CreateThread() can lead to memory leaks caused by C-runtime library.
* Advanced Windows by Richter pg. # 109. */
unsigned thread_id;
unsigned long real_handle =
_beginthreadex(NULL, 0, THREAD_ENTRYPOINT, THREAD_ARG, CREATE_SUSPENDED, &thread_id);
if (!real_handle)
{
Firebird::system_call_failed::raise("_beginthreadex", GetLastError());
}
HANDLE handle = reinterpret_cast<HANDLE>(real_handle);
SetThreadPriority(handle, priority);
ResumeThread(handle);
if (p_handle)
{
*p_handle = handle;
}
else
{
CloseHandle(handle);
}
}
void Thread::waitForCompletion(Handle& handle)
{
WaitForSingleObject(handle, 500);
CloseHandle(handle);
handle = 0;
}
void Thread::kill(Handle& handle)
{
TerminateThread(handle, -1);
CloseHandle(handle);
handle = 0;
}
ThreadId Thread::getId()
{
return GetCurrentThreadId();
}
void Thread::sleep(unsigned milliseconds)
{
SleepEx(milliseconds, FALSE);
}
void Thread::yield()
{
SleepEx(0, FALSE);
}
#endif // WIN_NT
#ifndef START_THREAD
void Thread::start(ThreadEntryPoint* routine, void* arg, int priority_arg, Handle* p_handle)
{
/**************************************
*
* t h r e a d _ s t a r t ( G e n e r i c )
*
**************************************
*
* Functional description
* Wrong attempt to start a new thread.
*
**************************************/
}
void Thread::waitForCompletion(Handle&)
{
}
void Thread::kill(Handle&)
{
}
Thread::Handle Thread::getId()
{
}
void Thread::sleep(unsigned milliseconds)
{
}
void Thread::yield()
{
}
#endif // START_THREAD