parent
2f302e93a9
commit
b74c3cc9fa
@ -1,97 +0,0 @@
|
|||||||
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
||||||
// Copyright (C) 2010 Winch Gate Property Limited
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as
|
|
||||||
// published by the Free Software Foundation, either version 3 of the
|
|
||||||
// License, or (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#ifndef NL_P_THREAD_H
|
|
||||||
#define NL_P_THREAD_H
|
|
||||||
|
|
||||||
#include "types_nl.h"
|
|
||||||
|
|
||||||
#ifdef NL_OS_UNIX
|
|
||||||
|
|
||||||
#include "thread.h"
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace NLMISC {
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Posix Thread
|
|
||||||
* \author Olivier Cado
|
|
||||||
* \author Nevrax France
|
|
||||||
* \date 2001
|
|
||||||
*/
|
|
||||||
class CPThread : public IThread
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum TThreadState
|
|
||||||
{
|
|
||||||
ThreadStateNone,
|
|
||||||
ThreadStateRunning,
|
|
||||||
ThreadStateFinished,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Constructor
|
|
||||||
CPThread( IRunnable *runnable, uint32 stackSize);
|
|
||||||
|
|
||||||
virtual ~CPThread();
|
|
||||||
|
|
||||||
virtual void start();
|
|
||||||
virtual bool isRunning();
|
|
||||||
virtual void terminate();
|
|
||||||
virtual void wait();
|
|
||||||
virtual bool setCPUMask(uint64 cpuMask);
|
|
||||||
virtual uint64 getCPUMask();
|
|
||||||
virtual void setPriority(TThreadPriority priority);
|
|
||||||
virtual std::string getUserName();
|
|
||||||
|
|
||||||
virtual IRunnable *getRunnable()
|
|
||||||
{
|
|
||||||
return Runnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal use
|
|
||||||
IRunnable *Runnable;
|
|
||||||
|
|
||||||
TThreadState _State;
|
|
||||||
pthread_t _ThreadHandle;
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint32 _StackSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Posix Process
|
|
||||||
* \author Cyril 'Hulud' Corvazier
|
|
||||||
* \author Nevrax France
|
|
||||||
* \date 2001
|
|
||||||
*/
|
|
||||||
class CPProcess : public IProcess
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~CPProcess() {}
|
|
||||||
virtual uint64 getCPUMask();
|
|
||||||
virtual bool setCPUMask(uint64 mask);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // NLMISC
|
|
||||||
|
|
||||||
|
|
||||||
#endif // NL_OS_UNIX
|
|
||||||
|
|
||||||
#endif // NL_P_THREAD_H
|
|
||||||
|
|
||||||
/* End of p_thread.h */
|
|
@ -1,145 +0,0 @@
|
|||||||
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
||||||
// Copyright (C) 2010 Winch Gate Property Limited
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as
|
|
||||||
// published by the Free Software Foundation, either version 3 of the
|
|
||||||
// License, or (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#ifndef NL_WIN_THREAD_H
|
|
||||||
#define NL_WIN_THREAD_H
|
|
||||||
|
|
||||||
#include "types_nl.h"
|
|
||||||
#include "thread.h"
|
|
||||||
|
|
||||||
#ifdef NL_OS_WINDOWS
|
|
||||||
|
|
||||||
namespace NLMISC {
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Windows implementation of CThread class (look thread.h)
|
|
||||||
* \author Vianney Lecroart
|
|
||||||
* \author Nevrax France
|
|
||||||
* \date 2000
|
|
||||||
*/
|
|
||||||
class CWinThread : public IThread
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// Constructor
|
|
||||||
CWinThread(IRunnable *runnable, uint32 stackSize);
|
|
||||||
|
|
||||||
/// Don't use this constructor, only used to initialise the main thread class
|
|
||||||
CWinThread (void* threadHandle, uint32 threadId);
|
|
||||||
|
|
||||||
virtual ~CWinThread();
|
|
||||||
|
|
||||||
virtual void start();
|
|
||||||
virtual bool isRunning();
|
|
||||||
virtual void terminate();
|
|
||||||
virtual void wait();
|
|
||||||
virtual bool setCPUMask(uint64 cpuMask);
|
|
||||||
virtual uint64 getCPUMask();
|
|
||||||
virtual void setPriority(TThreadPriority priority);
|
|
||||||
virtual std::string getUserName();
|
|
||||||
|
|
||||||
virtual IRunnable *getRunnable()
|
|
||||||
{
|
|
||||||
return Runnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Win32 specific
|
|
||||||
// Get the suspend count. Will be -1 is the thread hasn't been started yet
|
|
||||||
int getSuspendCount() const { return _SuspendCount; }
|
|
||||||
// Increment the suspend count, a suspend count >= 1 means the thread is suspended
|
|
||||||
void incSuspendCount();
|
|
||||||
/** Descrement the suspend count. Reaching 0 will resume the thread
|
|
||||||
* An assertion is raised is the suspend count is already 0
|
|
||||||
*/
|
|
||||||
void decSuspendCount();
|
|
||||||
// Suspend the thread. No-op if already suspended
|
|
||||||
void suspend();
|
|
||||||
// Resume the thread. No-op if already resumed
|
|
||||||
void resume();
|
|
||||||
// Priority boost
|
|
||||||
void enablePriorityBoost(bool enabled);
|
|
||||||
|
|
||||||
/// private use
|
|
||||||
IRunnable *Runnable;
|
|
||||||
|
|
||||||
private:
|
|
||||||
int _SuspendCount;
|
|
||||||
uint32 _StackSize;
|
|
||||||
void *ThreadHandle; // HANDLE don't put it to avoid including windows.h
|
|
||||||
uint32 ThreadId; // DWORD don't put it to avoid including windows.h
|
|
||||||
bool _MainThread; // true if ths thread is the main thread, else false
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Windows Process
|
|
||||||
* \author Cyril 'Hulud' Corvazier
|
|
||||||
* \author Nicolas Vizerie
|
|
||||||
* \author Nevrax France
|
|
||||||
* \date 2001, 2007
|
|
||||||
*/
|
|
||||||
class CWinProcess : public IProcess
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
CWinProcess (void *handle);
|
|
||||||
virtual ~CWinProcess() {} // TODO do something with _ProcessHandle?
|
|
||||||
|
|
||||||
virtual uint64 getCPUMask();
|
|
||||||
virtual bool setCPUMask(uint64 mask);
|
|
||||||
|
|
||||||
// processes helpers
|
|
||||||
static bool enumProcessesId(std::vector<uint32> &processesId);
|
|
||||||
// get fully qualified path for all modules used by a given process
|
|
||||||
static bool enumProcessModules(uint32 processId, std::vector<std::string> &moduleNames);
|
|
||||||
static uint32 getProcessIdFromModuleFilename(const std::string &moduleFileName);
|
|
||||||
static bool terminateProcess(uint32 processId, uint exitCode = 0);
|
|
||||||
static bool terminateProcessFromModuleName(const std::string &moduleName, uint exitCode = 0);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void *_ProcessHandle;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
// I didn't use and test that code, enventually, but maybe useful in the future
|
|
||||||
//
|
|
||||||
// Utility class to launch a process and check if it is still running.
|
|
||||||
// Implemented under windows only for now
|
|
||||||
//
|
|
||||||
class CProcessWatch
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CProcessWatch();
|
|
||||||
~CProcessWatch();
|
|
||||||
// launch a process with the given name and arguments, return true on success
|
|
||||||
bool launch(const std::string &programName, const std::string &arguments);
|
|
||||||
// return true if the process is still runing
|
|
||||||
bool isRunning() const;
|
|
||||||
private:
|
|
||||||
class CProcessWatchImpl *_PImpl;
|
|
||||||
};
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
} // NLMISC
|
|
||||||
|
|
||||||
#endif // NL_OS_WINDOWS
|
|
||||||
|
|
||||||
#endif // NL_WIN_THREAD_H
|
|
||||||
|
|
||||||
/* End of win_thread.h */
|
|
@ -1,387 +0,0 @@
|
|||||||
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
||||||
// Copyright (C) 2010 Winch Gate Property Limited
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as
|
|
||||||
// published by the Free Software Foundation, either version 3 of the
|
|
||||||
// License, or (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
#include "stdmisc.h"
|
|
||||||
|
|
||||||
#include <nel/misc/types_nl.h>
|
|
||||||
#include <nel/misc/debug.h>
|
|
||||||
|
|
||||||
#ifdef NL_OS_UNIX
|
|
||||||
|
|
||||||
#include "nel/misc/p_thread.h"
|
|
||||||
|
|
||||||
#include <sched.h>
|
|
||||||
#include <pwd.h>
|
|
||||||
|
|
||||||
#ifdef DEBUG_NEW
|
|
||||||
#define new DEBUG_NEW
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace NLMISC {
|
|
||||||
|
|
||||||
/* Key for thread specific storage holding IThread pointer. */
|
|
||||||
static pthread_key_t threadSpecificKey;
|
|
||||||
|
|
||||||
/* Special thread type representing the main thread. */
|
|
||||||
struct CPMainThread : public CPThread
|
|
||||||
{
|
|
||||||
CPMainThread() : CPThread(NULL, 0)
|
|
||||||
{
|
|
||||||
if(pthread_key_create(&threadSpecificKey, NULL) != 0)
|
|
||||||
throw EThread("cannot create thread specific storage key.");
|
|
||||||
|
|
||||||
if(pthread_setspecific(threadSpecificKey, this) != 0)
|
|
||||||
throw EThread("cannot set main thread ptr in thread specific storage.");
|
|
||||||
}
|
|
||||||
|
|
||||||
~CPMainThread()
|
|
||||||
{
|
|
||||||
if(pthread_key_delete(threadSpecificKey) != 0)
|
|
||||||
throw EThread("cannot delete thread specific storage key.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Holds the thread instance representing the main thread. */
|
|
||||||
static CPMainThread mainThread = CPMainThread();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The IThread static creator
|
|
||||||
*/
|
|
||||||
IThread *IThread::create( IRunnable *runnable, uint32 stackSize)
|
|
||||||
{
|
|
||||||
return new CPThread( runnable, stackSize );
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the current thread
|
|
||||||
*/
|
|
||||||
IThread *IThread::getCurrentThread ()
|
|
||||||
{
|
|
||||||
return (IThread *)pthread_getspecific(threadSpecificKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Thread beginning
|
|
||||||
*/
|
|
||||||
static void *ProxyFunc( void *arg )
|
|
||||||
{
|
|
||||||
CPThread *parent = (CPThread*)arg;
|
|
||||||
|
|
||||||
// Set this thread's thread specific storage to IThread instance pointer
|
|
||||||
if(pthread_setspecific(threadSpecificKey, parent) != 0)
|
|
||||||
throw EThread("cannot set thread ptr in thread specific storage.");
|
|
||||||
|
|
||||||
// Allow to terminate the thread without cancellation point
|
|
||||||
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
|
|
||||||
|
|
||||||
// Run the code of the thread
|
|
||||||
parent->Runnable->run();
|
|
||||||
|
|
||||||
{
|
|
||||||
pthread_t thread_self = pthread_self();
|
|
||||||
// Make sure the parent still cares
|
|
||||||
// If this thread was replaced with a new thread (which should not happen),
|
|
||||||
// and the IThread object has been deleted, this will likely crash.
|
|
||||||
if (parent->_ThreadHandle == thread_self)
|
|
||||||
parent->_State = CPThread::ThreadStateFinished;
|
|
||||||
else
|
|
||||||
throw EThread("Thread ended after being detached, this should not happen");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow some clean
|
|
||||||
// pthread_exit(0);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
CPThread::CPThread(IRunnable *runnable, uint32 stackSize)
|
|
||||||
: Runnable(runnable),
|
|
||||||
_State(ThreadStateNone),
|
|
||||||
_StackSize(stackSize)
|
|
||||||
{}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Destructor
|
|
||||||
*/
|
|
||||||
CPThread::~CPThread()
|
|
||||||
{
|
|
||||||
terminate(); // force the end of the thread if not already ended
|
|
||||||
|
|
||||||
if (_State != ThreadStateNone)
|
|
||||||
pthread_detach(_ThreadHandle); // free allocated resources only if it was created
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* start
|
|
||||||
*/
|
|
||||||
void CPThread::start()
|
|
||||||
{
|
|
||||||
pthread_attr_t tattr;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (_StackSize != 0)
|
|
||||||
{
|
|
||||||
/* initialized with default attributes */
|
|
||||||
ret = pthread_attr_init(&tattr);
|
|
||||||
|
|
||||||
/* setting the size of the stack also */
|
|
||||||
ret = pthread_attr_setstacksize(&tattr, _StackSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool detach_old_thread = false;
|
|
||||||
pthread_t old_thread_handle;
|
|
||||||
if (_State != ThreadStateNone)
|
|
||||||
{
|
|
||||||
if (_State == ThreadStateRunning)
|
|
||||||
{
|
|
||||||
// I don't know if this behaviour is allowed, but neither thread implementations
|
|
||||||
// check the start function, and both simply let the existing running thread for what it is...
|
|
||||||
// From now on, this is not allowed.
|
|
||||||
throw EThread("Starting a thread that is already started, existing thread will continue running, this should not happen");
|
|
||||||
}
|
|
||||||
detach_old_thread = true;
|
|
||||||
old_thread_handle = _ThreadHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pthread_create(&_ThreadHandle, _StackSize != 0 ? &tattr : NULL, ProxyFunc, this) != 0)
|
|
||||||
{
|
|
||||||
throw EThread("Cannot start new thread");
|
|
||||||
}
|
|
||||||
_State = ThreadStateRunning;
|
|
||||||
|
|
||||||
if (detach_old_thread)
|
|
||||||
{
|
|
||||||
// Docs don't say anything about what happens when pthread_create is called with existing handle referenced.
|
|
||||||
if (old_thread_handle == _ThreadHandle)
|
|
||||||
throw EThread("Thread handle did not change, this should not happen");
|
|
||||||
// Don't care about old thread, free resources when it terminates.
|
|
||||||
pthread_detach(old_thread_handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CPThread::isRunning()
|
|
||||||
{
|
|
||||||
return _State == ThreadStateRunning;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* terminate
|
|
||||||
*/
|
|
||||||
void CPThread::terminate()
|
|
||||||
{
|
|
||||||
if (_State == ThreadStateRunning)
|
|
||||||
{
|
|
||||||
// cancel only if started
|
|
||||||
pthread_cancel(_ThreadHandle);
|
|
||||||
_State = ThreadStateFinished; // set to finished
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* wait
|
|
||||||
*/
|
|
||||||
void CPThread::wait ()
|
|
||||||
{
|
|
||||||
if (_State == ThreadStateRunning)
|
|
||||||
{
|
|
||||||
int error = pthread_join(_ThreadHandle, 0);
|
|
||||||
switch (error)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case EINVAL:
|
|
||||||
throw EThread("Thread is not joinable");
|
|
||||||
case ESRCH:
|
|
||||||
throw EThread("No thread found with this id");
|
|
||||||
case EDEADLK:
|
|
||||||
throw EThread("Deadlock detected or calling thread waits for itself");
|
|
||||||
default:
|
|
||||||
throw EThread("Unknown thread join error");
|
|
||||||
}
|
|
||||||
if(_State != ThreadStateFinished)
|
|
||||||
throw EThread("Thread did not finish, this should not happen");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* setCPUMask
|
|
||||||
*/
|
|
||||||
bool CPThread::setCPUMask(uint64 cpuMask)
|
|
||||||
{
|
|
||||||
#ifdef __USE_GNU
|
|
||||||
|
|
||||||
nlwarning("This code does not work. May cause a segmentation fault...");
|
|
||||||
|
|
||||||
sint res = pthread_setaffinity_np(_ThreadHandle, sizeof(uint64), (const cpu_set_t*)&cpuMask);
|
|
||||||
|
|
||||||
if (res)
|
|
||||||
{
|
|
||||||
nlwarning("pthread_setaffinity_np() returned %d", res);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
#else // __USE_GNU
|
|
||||||
|
|
||||||
return false;
|
|
||||||
|
|
||||||
#endif // __USE_GNU
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* getCPUMask
|
|
||||||
*/
|
|
||||||
uint64 CPThread::getCPUMask()
|
|
||||||
{
|
|
||||||
#ifdef __USE_GNU
|
|
||||||
|
|
||||||
nlwarning("This code does not work. May cause a segmentation fault...");
|
|
||||||
|
|
||||||
uint64 cpuMask = 0;
|
|
||||||
|
|
||||||
sint res = pthread_getaffinity_np(_ThreadHandle, sizeof(uint64), (cpu_set_t*)&cpuMask);
|
|
||||||
|
|
||||||
if (res)
|
|
||||||
{
|
|
||||||
nlwarning("pthread_getaffinity_np() returned %d", res);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cpuMask;
|
|
||||||
|
|
||||||
#else // __USE_GNU
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
#endif // __USE_GNU
|
|
||||||
}
|
|
||||||
|
|
||||||
void CPThread::setPriority(TThreadPriority priority)
|
|
||||||
{
|
|
||||||
// TODO: Test this
|
|
||||||
sched_param sp;
|
|
||||||
switch (priority)
|
|
||||||
{
|
|
||||||
case ThreadPriorityHigh:
|
|
||||||
{
|
|
||||||
int minPrio = sched_get_priority_min(SCHED_FIFO);
|
|
||||||
int maxPrio = sched_get_priority_max(SCHED_FIFO);
|
|
||||||
sp.sched_priority = ((maxPrio - minPrio) / 4) + minPrio;
|
|
||||||
pthread_setschedparam(_ThreadHandle, SCHED_FIFO, &sp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ThreadPriorityHighest:
|
|
||||||
{
|
|
||||||
int minPrio = sched_get_priority_min(SCHED_FIFO);
|
|
||||||
int maxPrio = sched_get_priority_max(SCHED_FIFO);
|
|
||||||
sp.sched_priority = ((maxPrio - minPrio) / 2) + minPrio;
|
|
||||||
pthread_setschedparam(_ThreadHandle, SCHED_FIFO, &sp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
sp.sched_priority = 0;
|
|
||||||
pthread_setschedparam(_ThreadHandle, SCHED_OTHER, &sp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* getUserName
|
|
||||||
*/
|
|
||||||
std::string CPThread::getUserName()
|
|
||||||
{
|
|
||||||
struct passwd *pw = getpwuid(getuid());
|
|
||||||
|
|
||||||
if (!pw)
|
|
||||||
return "";
|
|
||||||
|
|
||||||
return pw->pw_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// **** Process
|
|
||||||
|
|
||||||
// The current process
|
|
||||||
CPProcess CurrentProcess;
|
|
||||||
|
|
||||||
// Get the current process
|
|
||||||
IProcess *IProcess::getCurrentProcess ()
|
|
||||||
{
|
|
||||||
return &CurrentProcess;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* getCPUMask
|
|
||||||
*/
|
|
||||||
uint64 CPProcess::getCPUMask()
|
|
||||||
{
|
|
||||||
#ifdef __USE_GNU
|
|
||||||
|
|
||||||
uint64 cpuMask = 0;
|
|
||||||
sint res = sched_getaffinity(getpid(), sizeof(uint64), (cpu_set_t*)&cpuMask);
|
|
||||||
|
|
||||||
if (res)
|
|
||||||
{
|
|
||||||
nlwarning("sched_getaffinity() returned %d, errno = %d: %s", res, errno, strerror(errno));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cpuMask;
|
|
||||||
|
|
||||||
#else // __USE_GNU
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
#endif // __USE_GNU
|
|
||||||
}
|
|
||||||
|
|
||||||
/// set the CPU mask
|
|
||||||
bool CPProcess::setCPUMask(uint64 cpuMask)
|
|
||||||
{
|
|
||||||
#ifdef __USE_GNU
|
|
||||||
|
|
||||||
sint res = sched_setaffinity(getpid(), sizeof(uint64), (const cpu_set_t*)&cpuMask);
|
|
||||||
|
|
||||||
if (res)
|
|
||||||
{
|
|
||||||
nlwarning("sched_setaffinity() returned %d, errno = %d: %s", res, errno, strerror(errno));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
#else // __USE_GNU
|
|
||||||
|
|
||||||
return false;
|
|
||||||
|
|
||||||
#endif // __USE_GNU
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // NLMISC
|
|
||||||
|
|
||||||
#else // NL_OS_UNIX
|
|
||||||
|
|
||||||
// remove stupid VC6 warnings
|
|
||||||
void foo_p_thread_cpp() {}
|
|
||||||
|
|
||||||
#endif // NL_OS_UNIX
|
|
@ -0,0 +1,105 @@
|
|||||||
|
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||||||
|
// Copyright (C) 2010 Winch Gate Property Limited
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "stdmisc.h"
|
||||||
|
#include "nel/misc/thread.h"
|
||||||
|
#include "nel/misc/debug.h"
|
||||||
|
|
||||||
|
#include <SDL_thread.h>
|
||||||
|
|
||||||
|
namespace NLMISC {
|
||||||
|
|
||||||
|
CThread::CThread(IRunnable *runnable) : m_IsRunning(false), m_SDLThread(NULL), m_Runnable(runnable)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CThread::~CThread ()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
NOTE: Detach not supported, due to m_IsRunning which is referenced by the running thread
|
||||||
|
FIX: Use a thread-safe reference counted object for the m_IsRunning flag
|
||||||
|
if (m_SDLThread)
|
||||||
|
{
|
||||||
|
SDL_DetachThread(m_SDLThread);
|
||||||
|
}
|
||||||
|
NOTE: Currently use wait, which is much saner behaviour.
|
||||||
|
*/
|
||||||
|
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThread::start()
|
||||||
|
{
|
||||||
|
nlassert(!m_SDLThread);
|
||||||
|
nlassert(!m_IsRunning);
|
||||||
|
std::string name;
|
||||||
|
m_Runnable->getName(name);
|
||||||
|
m_IsRunning = true;
|
||||||
|
m_SDLThread = SDL_CreateThread(run, name.c_str(), (void *)this);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CThread::run(void *ptr)
|
||||||
|
{
|
||||||
|
NLMISC::CThread *thread = static_cast<NLMISC::CThread *>(ptr);
|
||||||
|
thread->m_Runnable->run();
|
||||||
|
thread->m_IsRunning = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThread::setPriority(TThreadPriority priority)
|
||||||
|
{
|
||||||
|
switch (priority)
|
||||||
|
{
|
||||||
|
case ThreadPriorityLow:
|
||||||
|
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW);
|
||||||
|
break;
|
||||||
|
case ThreadPriorityNormal:
|
||||||
|
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_NORMAL);
|
||||||
|
break;
|
||||||
|
case ThreadPriorityHigh:
|
||||||
|
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
nlerror("Invalid thread priority");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CThread::isRunning()
|
||||||
|
{
|
||||||
|
return m_IsRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CThread::wait()
|
||||||
|
{
|
||||||
|
m_WaitMutex.enter();
|
||||||
|
if (m_SDLThread)
|
||||||
|
{
|
||||||
|
SDL_WaitThread(m_SDLThread, &m_ThreadResult);
|
||||||
|
m_SDLThread = NULL;
|
||||||
|
nlassert(!m_IsRunning);
|
||||||
|
}
|
||||||
|
m_WaitMutex.leave();
|
||||||
|
return m_ThreadResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
IRunnable *CThread::getRunnable()
|
||||||
|
{
|
||||||
|
return m_Runnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // NLMISC
|
@ -1,609 +0,0 @@
|
|||||||
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
||||||
// Copyright (C) 2010 Winch Gate Property Limited
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as
|
|
||||||
// published by the Free Software Foundation, either version 3 of the
|
|
||||||
// License, or (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include "stdmisc.h"
|
|
||||||
#include "nel/misc/win_thread.h"
|
|
||||||
|
|
||||||
#ifdef NL_OS_WINDOWS
|
|
||||||
|
|
||||||
#include "nel/misc/path.h"
|
|
||||||
#define NOMINMAX
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
#include <typeinfo>
|
|
||||||
|
|
||||||
#ifdef DEBUG_NEW
|
|
||||||
#define new DEBUG_NEW
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace NLMISC {
|
|
||||||
|
|
||||||
CWinThread MainThread ((void*)GetCurrentThread (), GetCurrentThreadId());
|
|
||||||
DWORD TLSThreadPointer = 0xFFFFFFFF;
|
|
||||||
|
|
||||||
// the IThread static creator
|
|
||||||
IThread *IThread::create (IRunnable *runnable, uint32 stackSize)
|
|
||||||
{
|
|
||||||
return new CWinThread (runnable, stackSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
IThread *IThread::getCurrentThread ()
|
|
||||||
{
|
|
||||||
// TLS alloc must have been done
|
|
||||||
nlassert (TLSThreadPointer != 0xffffffff);
|
|
||||||
|
|
||||||
// Get the thread pointer
|
|
||||||
IThread *thread = (IThread*)TlsGetValue (TLSThreadPointer);
|
|
||||||
|
|
||||||
// Return current thread
|
|
||||||
return thread;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned long __stdcall ProxyFunc (void *arg)
|
|
||||||
{
|
|
||||||
CWinThread *parent = (CWinThread *) arg;
|
|
||||||
|
|
||||||
// TLS alloc must have been done
|
|
||||||
nlassert (TLSThreadPointer != 0xffffffff);
|
|
||||||
|
|
||||||
// Set the thread pointer in TLS memory
|
|
||||||
nlverify (TlsSetValue (TLSThreadPointer, (void*)parent) != 0);
|
|
||||||
|
|
||||||
// Run the thread
|
|
||||||
parent->Runnable->run();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
CWinThread::CWinThread (IRunnable *runnable, uint32 stackSize)
|
|
||||||
{
|
|
||||||
_StackSize = stackSize;
|
|
||||||
this->Runnable = runnable;
|
|
||||||
ThreadHandle = NULL;
|
|
||||||
_SuspendCount = -1;
|
|
||||||
_MainThread = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
class CWinCriticalSection
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
CRITICAL_SECTION cs;
|
|
||||||
public:
|
|
||||||
CWinCriticalSection() { InitializeCriticalSection(&cs); }
|
|
||||||
~CWinCriticalSection() { DeleteCriticalSection(&cs); }
|
|
||||||
inline void enter() { EnterCriticalSection(&cs); }
|
|
||||||
inline void leave() { LeaveCriticalSection(&cs); }
|
|
||||||
};
|
|
||||||
CWinCriticalSection s_CS;
|
|
||||||
}/* anonymous namespace */
|
|
||||||
|
|
||||||
CWinThread::CWinThread (void* threadHandle, uint32 threadId)
|
|
||||||
{
|
|
||||||
// Main thread
|
|
||||||
_MainThread = true;
|
|
||||||
this->Runnable = NULL;
|
|
||||||
ThreadHandle = threadHandle;
|
|
||||||
ThreadId = threadId;
|
|
||||||
|
|
||||||
// TLS alloc must have been done
|
|
||||||
TLSThreadPointer = TlsAlloc ();
|
|
||||||
nlassert (TLSThreadPointer!=0xffffffff);
|
|
||||||
|
|
||||||
// Set the thread pointer in TLS memory
|
|
||||||
nlverify (TlsSetValue (TLSThreadPointer, (void*)this) != 0);
|
|
||||||
|
|
||||||
if (GetCurrentThreadId() == threadId)
|
|
||||||
{
|
|
||||||
_SuspendCount = 0; // is calling thread call this itself, well, if we reach this place
|
|
||||||
// there are chances that it is not suspended ...
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// initialized from another thread (very unlikely ...)
|
|
||||||
nlassert(0); // WARNING: following code has not tested! don't know if it work fo real ...
|
|
||||||
// This is just a suggestion of a possible solution, should this situation one day occur ...
|
|
||||||
// Ensure that this thread don't get deleted, or we could suspend the main thread
|
|
||||||
s_CS.enter();
|
|
||||||
// the 2 following statement must be executed atomicaly among the threads of the current process !
|
|
||||||
SuspendThread(threadHandle);
|
|
||||||
_SuspendCount = ResumeThread(threadHandle);
|
|
||||||
s_CS.leave();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void CWinThread::incSuspendCount()
|
|
||||||
{
|
|
||||||
nlassert(ThreadHandle); // start was not called !!
|
|
||||||
int newSuspendCount = ::SuspendThread(ThreadHandle) + 1;
|
|
||||||
nlassert(newSuspendCount != 0xffffffff); // more infos with 'GetLastError'
|
|
||||||
nlassert(newSuspendCount == _SuspendCount + 1); // is this assert fire , then 'SuspendThread' or 'ResumeThread'
|
|
||||||
// have been called outside of this object interface! (on this thread handle ...)
|
|
||||||
_SuspendCount = newSuspendCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CWinThread::decSuspendCount()
|
|
||||||
{
|
|
||||||
nlassert(ThreadHandle); // 'start' was not called !!
|
|
||||||
nlassert(_SuspendCount > 0);
|
|
||||||
int newSuspendCount = ::ResumeThread(ThreadHandle) - 1;
|
|
||||||
nlassert(newSuspendCount != 0xffffffff); // more infos with 'GetLastError'
|
|
||||||
nlassert(newSuspendCount == _SuspendCount - 1); // is this assert fire , then 'SuspendThread' or 'ResumeThread'
|
|
||||||
// have been called outside of this object interface! (on this thread handle ...)
|
|
||||||
_SuspendCount = newSuspendCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CWinThread::suspend()
|
|
||||||
{
|
|
||||||
if (getSuspendCount() == 0)
|
|
||||||
{
|
|
||||||
incSuspendCount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CWinThread::resume()
|
|
||||||
{
|
|
||||||
while (getSuspendCount() != 0)
|
|
||||||
{
|
|
||||||
decSuspendCount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CWinThread::setPriority(TThreadPriority priority)
|
|
||||||
{
|
|
||||||
nlassert(ThreadHandle); // 'start' was not called !!
|
|
||||||
BOOL result = SetThreadPriority(ThreadHandle, (int)priority);
|
|
||||||
nlassert(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CWinThread::enablePriorityBoost(bool enabled)
|
|
||||||
{
|
|
||||||
nlassert(ThreadHandle); // 'start' was not called !!
|
|
||||||
SetThreadPriorityBoost(ThreadHandle, enabled ? TRUE : FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
CWinThread::~CWinThread ()
|
|
||||||
{
|
|
||||||
// If not the main thread
|
|
||||||
if (_MainThread)
|
|
||||||
{
|
|
||||||
// Free TLS memory
|
|
||||||
nlassert (TLSThreadPointer!=0xffffffff);
|
|
||||||
TlsFree (TLSThreadPointer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (ThreadHandle != NULL) terminate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CWinThread::start ()
|
|
||||||
{
|
|
||||||
if (isRunning())
|
|
||||||
throw EThread("Starting a thread that is already started, existing thread will continue running, this should not happen");
|
|
||||||
|
|
||||||
// ThreadHandle = (void *) ::CreateThread (NULL, _StackSize, ProxyFunc, this, 0, (DWORD *)&ThreadId);
|
|
||||||
ThreadHandle = (void *) ::CreateThread (NULL, 0, ProxyFunc, this, 0, (DWORD *)&ThreadId);
|
|
||||||
// nldebug("NLMISC: thread %x started for runnable '%x'", typeid( Runnable ).name());
|
|
||||||
// OutputDebugString(toString(NL_LOC_MSG " NLMISC: thread %x started for runnable '%s'\n", ThreadId, typeid( *Runnable ).name()).c_str());
|
|
||||||
SetThreadPriorityBoost (ThreadHandle, TRUE); // FALSE == Enable Priority Boost
|
|
||||||
if (ThreadHandle == NULL)
|
|
||||||
{
|
|
||||||
throw EThread ( "Cannot create new thread" );
|
|
||||||
}
|
|
||||||
|
|
||||||
_SuspendCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CWinThread::isRunning()
|
|
||||||
{
|
|
||||||
if (ThreadHandle == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
DWORD exitCode;
|
|
||||||
if (!GetExitCodeThread(ThreadHandle, &exitCode))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return exitCode == STILL_ACTIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void CWinThread::terminate ()
|
|
||||||
{
|
|
||||||
TerminateThread((HANDLE)ThreadHandle, 0);
|
|
||||||
CloseHandle((HANDLE)ThreadHandle);
|
|
||||||
ThreadHandle = NULL;
|
|
||||||
_SuspendCount = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CWinThread::wait ()
|
|
||||||
{
|
|
||||||
if (ThreadHandle == NULL) return;
|
|
||||||
|
|
||||||
WaitForSingleObject(ThreadHandle, INFINITE);
|
|
||||||
CloseHandle(ThreadHandle);
|
|
||||||
ThreadHandle = NULL;
|
|
||||||
_SuspendCount = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CWinThread::setCPUMask(uint64 cpuMask)
|
|
||||||
{
|
|
||||||
// Thread must exist
|
|
||||||
if (ThreadHandle == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Ask the system for number of processor available for this process
|
|
||||||
return SetThreadAffinityMask ((HANDLE)ThreadHandle, (DWORD_PTR)cpuMask) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64 CWinThread::getCPUMask()
|
|
||||||
{
|
|
||||||
// Thread must exist
|
|
||||||
if (ThreadHandle == NULL)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
// Get the current process mask
|
|
||||||
uint64 mask=IProcess::getCurrentProcess ()->getCPUMask ();
|
|
||||||
|
|
||||||
// Get thread affinity mask
|
|
||||||
DWORD_PTR old = SetThreadAffinityMask ((HANDLE)ThreadHandle, (DWORD_PTR)mask);
|
|
||||||
nlassert (old != 0);
|
|
||||||
if (old == 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
// Reset it
|
|
||||||
SetThreadAffinityMask ((HANDLE)ThreadHandle, old);
|
|
||||||
|
|
||||||
// Return the mask
|
|
||||||
return (uint64)old;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CWinThread::getUserName()
|
|
||||||
{
|
|
||||||
char userName[512];
|
|
||||||
DWORD size = 512;
|
|
||||||
GetUserName (userName, &size);
|
|
||||||
return (const char*)userName;
|
|
||||||
}
|
|
||||||
|
|
||||||
// **** Process
|
|
||||||
|
|
||||||
// The current process
|
|
||||||
CWinProcess CurrentProcess ((void*)GetCurrentProcess());
|
|
||||||
|
|
||||||
// Get the current process
|
|
||||||
IProcess *IProcess::getCurrentProcess ()
|
|
||||||
{
|
|
||||||
return &CurrentProcess;
|
|
||||||
}
|
|
||||||
|
|
||||||
CWinProcess::CWinProcess (void *handle)
|
|
||||||
{
|
|
||||||
// Get the current process handle
|
|
||||||
_ProcessHandle = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64 CWinProcess::getCPUMask()
|
|
||||||
{
|
|
||||||
// Ask the system for number of processor available for this process
|
|
||||||
DWORD_PTR processAffinityMask;
|
|
||||||
DWORD_PTR systemAffinityMask;
|
|
||||||
if (GetProcessAffinityMask((HANDLE)_ProcessHandle, &processAffinityMask, &systemAffinityMask))
|
|
||||||
{
|
|
||||||
// Return the CPU mask
|
|
||||||
return (uint64)processAffinityMask;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CWinProcess::setCPUMask(uint64 mask)
|
|
||||||
{
|
|
||||||
// Ask the system for number of processor available for this process
|
|
||||||
DWORD_PTR processAffinityMask= (DWORD_PTR)mask;
|
|
||||||
return SetProcessAffinityMask((HANDLE)_ProcessHandle, processAffinityMask)!=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ****************************************************************************************************************
|
|
||||||
/**
|
|
||||||
* Simple wrapper around the PSAPI library
|
|
||||||
* \author Nicolas Vizerie
|
|
||||||
* \author GameForge
|
|
||||||
* \date 2007
|
|
||||||
*/
|
|
||||||
|
|
||||||
class CPSAPILib
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef BOOL (WINAPI *EnumProcessesFunPtr)(DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded);
|
|
||||||
typedef DWORD (WINAPI *GetModuleFileNameExAFunPtr)(HANDLE hProcess, HMODULE hModule, LPTSTR lpFilename, DWORD nSize);
|
|
||||||
typedef BOOL (WINAPI *EnumProcessModulesFunPtr)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded);
|
|
||||||
EnumProcessesFunPtr EnumProcesses;
|
|
||||||
GetModuleFileNameExAFunPtr GetModuleFileNameExA;
|
|
||||||
EnumProcessModulesFunPtr EnumProcessModules;
|
|
||||||
public:
|
|
||||||
CPSAPILib();
|
|
||||||
~CPSAPILib();
|
|
||||||
bool init();
|
|
||||||
private:
|
|
||||||
HINSTANCE _PSAPILibHandle;
|
|
||||||
bool _LoadFailed;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ****************************************************************************************************************
|
|
||||||
CPSAPILib::CPSAPILib()
|
|
||||||
{
|
|
||||||
_LoadFailed = false;
|
|
||||||
_PSAPILibHandle = NULL;
|
|
||||||
EnumProcesses = NULL;
|
|
||||||
GetModuleFileNameExA = NULL;
|
|
||||||
EnumProcessModules = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ****************************************************************************************************************
|
|
||||||
CPSAPILib::~CPSAPILib()
|
|
||||||
{
|
|
||||||
if (_PSAPILibHandle)
|
|
||||||
{
|
|
||||||
FreeLibrary(_PSAPILibHandle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ****************************************************************************************************************
|
|
||||||
bool CPSAPILib::init()
|
|
||||||
{
|
|
||||||
//
|
|
||||||
if (_LoadFailed) return false;
|
|
||||||
if (!_PSAPILibHandle)
|
|
||||||
{
|
|
||||||
_PSAPILibHandle = LoadLibrary("psapi.dll");
|
|
||||||
if (!_PSAPILibHandle)
|
|
||||||
{
|
|
||||||
nlwarning("couldn't load psapi.dll, possibly not supported by os");
|
|
||||||
_LoadFailed = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
EnumProcesses = (EnumProcessesFunPtr) GetProcAddress(_PSAPILibHandle, "EnumProcesses");
|
|
||||||
GetModuleFileNameExA = (GetModuleFileNameExAFunPtr) GetProcAddress(_PSAPILibHandle, "GetModuleFileNameExA");
|
|
||||||
EnumProcessModules = (EnumProcessModulesFunPtr) GetProcAddress(_PSAPILibHandle, "EnumProcessModules");
|
|
||||||
if (!EnumProcesses ||
|
|
||||||
!GetModuleFileNameExA ||
|
|
||||||
!EnumProcessModules
|
|
||||||
)
|
|
||||||
{
|
|
||||||
nlwarning("Failed to import functions from psapi.dll!");
|
|
||||||
_LoadFailed = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static CPSAPILib PSAPILib;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ****************************************************************************************************************
|
|
||||||
bool CWinProcess::enumProcessesId(std::vector<uint32> &processesId)
|
|
||||||
{
|
|
||||||
if (!PSAPILib.init()) return false;
|
|
||||||
// list of processes
|
|
||||||
std::vector<uint32> prcIds(16);
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
DWORD cbNeeded;
|
|
||||||
if (!PSAPILib.EnumProcesses((DWORD *) &prcIds[0], (DWORD)(prcIds.size() * sizeof(DWORD)), &cbNeeded))
|
|
||||||
{
|
|
||||||
nlwarning("Processes enumeration failed!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (cbNeeded < prcIds.size() * sizeof(DWORD))
|
|
||||||
{
|
|
||||||
prcIds.resize(cbNeeded / sizeof(DWORD));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// make some more room
|
|
||||||
prcIds.resize(prcIds.size() * 2);
|
|
||||||
}
|
|
||||||
processesId.swap(prcIds);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ****************************************************************************************************************
|
|
||||||
bool CWinProcess::enumProcessModules(uint32 processId, std::vector<std::string> &moduleNames)
|
|
||||||
{
|
|
||||||
if (!PSAPILib.init()) return false;
|
|
||||||
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, FALSE, (DWORD) processId);
|
|
||||||
if (!hProcess) return false;
|
|
||||||
// list of modules
|
|
||||||
std::vector<HMODULE> prcModules(2);
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
DWORD cbNeeded;
|
|
||||||
if (!PSAPILib.EnumProcessModules(hProcess, (HMODULE *) &prcModules[0], (DWORD)(prcModules.size() * sizeof(HMODULE)), &cbNeeded))
|
|
||||||
{
|
|
||||||
//nlwarning("Processe modules enumeration failed!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (cbNeeded < prcModules.size() * sizeof(HMODULE))
|
|
||||||
{
|
|
||||||
prcModules.resize(cbNeeded / sizeof(HMODULE));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// make some more room
|
|
||||||
prcModules.resize(prcModules.size() * 2);
|
|
||||||
}
|
|
||||||
moduleNames.clear();
|
|
||||||
std::vector<std::string> resultModuleNames;
|
|
||||||
char moduleName[MAX_PATH + 1];
|
|
||||||
for (uint m = 0; m < prcModules.size(); ++m)
|
|
||||||
{
|
|
||||||
if (PSAPILib.GetModuleFileNameExA(hProcess, prcModules[m], moduleName, MAX_PATH))
|
|
||||||
{
|
|
||||||
moduleNames.push_back(moduleName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CloseHandle(hProcess);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ****************************************************************************************************************
|
|
||||||
uint32 CWinProcess::getProcessIdFromModuleFilename(const std::string &moduleFileName)
|
|
||||||
{
|
|
||||||
std::vector<uint32> processesId;
|
|
||||||
if (!enumProcessesId(processesId)) return false;
|
|
||||||
std::vector<std::string> moduleNames;
|
|
||||||
for (uint prc = 0; prc < processesId.size(); ++prc)
|
|
||||||
{
|
|
||||||
if (enumProcessModules(processesId[prc], moduleNames))
|
|
||||||
{
|
|
||||||
for (uint m = 0; m < moduleNames.size(); ++m)
|
|
||||||
{
|
|
||||||
if (nlstricmp(CFile::getFilename(moduleNames[m]), moduleFileName) == 0)
|
|
||||||
{
|
|
||||||
return processesId[prc];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ****************************************************************************************************************
|
|
||||||
bool CWinProcess::terminateProcess(uint32 processId, uint exitCode)
|
|
||||||
{
|
|
||||||
if (!processId) return false;
|
|
||||||
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, (DWORD) processId);
|
|
||||||
if (!hProcess) return false;
|
|
||||||
BOOL ok = TerminateProcess(hProcess, (UINT) exitCode);
|
|
||||||
CloseHandle(hProcess);
|
|
||||||
return ok != FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ****************************************************************************************************************
|
|
||||||
bool CWinProcess::terminateProcessFromModuleName(const std::string &moduleName, uint exitCode)
|
|
||||||
{
|
|
||||||
return terminateProcess(getProcessIdFromModuleFilename(moduleName), exitCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////
|
|
||||||
// CProcessWatch //
|
|
||||||
///////////////////
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
// I didn't use and test that code, eventually, but maybe useful in the future
|
|
||||||
|
|
||||||
class CProcessWatchTask : public IRunnable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
HANDLE HProcess;
|
|
||||||
public:
|
|
||||||
CProcessWatchTask(HANDLE hProcess) : HProcess(hProcess)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
virtual void run()
|
|
||||||
{
|
|
||||||
WaitForSingleObject(HProcess, INFINITE);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CProcessWatchImpl
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool Launched;
|
|
||||||
IThread *WatchThread;
|
|
||||||
CProcessWatchTask *WatchTask;
|
|
||||||
public:
|
|
||||||
CProcessWatchImpl() : Launched(false), WatchThread(NULL), WatchTask(NULL)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
~CProcessWatchImpl()
|
|
||||||
{
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
void reset()
|
|
||||||
{
|
|
||||||
if (WatchThread)
|
|
||||||
{
|
|
||||||
if (WatchThread->isRunning())
|
|
||||||
{
|
|
||||||
WatchThread->terminate();
|
|
||||||
}
|
|
||||||
delete WatchTask;
|
|
||||||
delete WatchThread;
|
|
||||||
WatchTask = NULL;
|
|
||||||
WatchThread = NULL;
|
|
||||||
Launched = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool launch(const std::string &programName, const std::string &arguments)
|
|
||||||
{
|
|
||||||
if (isRunning()) return false;
|
|
||||||
PROCESS_INFORMATION processInfo;
|
|
||||||
STARTUPINFO startupInfo = {0};
|
|
||||||
startupInfo.cb = sizeof(STARTUPINFO);
|
|
||||||
if (CreateProcess(programName.c_str(), const_cast<LPTSTR>(arguments.c_str()), NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo))
|
|
||||||
{
|
|
||||||
WatchTask = new CProcessWatchTask(processInfo.hProcess);
|
|
||||||
WatchThread = IThread::create(WatchTask);
|
|
||||||
WatchThread->start();
|
|
||||||
Launched = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool isRunning()
|
|
||||||
{
|
|
||||||
if (!Launched) return false;
|
|
||||||
nlassert(WatchThread);
|
|
||||||
nlassert(WatchTask);
|
|
||||||
if (WatchThread->isRunning()) return true;
|
|
||||||
reset();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
CProcessWatch::CProcessWatch()
|
|
||||||
{
|
|
||||||
_PImpl = new CProcessWatchImpl;
|
|
||||||
}
|
|
||||||
|
|
||||||
CProcessWatch::~CProcessWatch()
|
|
||||||
{
|
|
||||||
delete _PImpl;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CProcessWatch::launch(const std::string &programName, const std::string &arguments)
|
|
||||||
{
|
|
||||||
return _PImpl->launch(programName, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CProcessWatch::isRunning() const
|
|
||||||
{
|
|
||||||
return _PImpl->isRunning();
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
} // NLMISC
|
|
||||||
|
|
||||||
#endif // NL_OS_WINDOWS
|
|
Loading…
Reference in New Issue