osxThread.mm 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #import <pthread.h>
  23. #import <stdlib.h>
  24. #import <errno.h>
  25. #import "memory/safeDelete.h"
  26. #import "platform/threads/thread.h"
  27. #import "platform/platformSemaphore.h"
  28. #import "platform/threads/mutex.h"
  29. #import "console/console.h"
  30. //-----------------------------------------------------------------------------
  31. #pragma mark ---- Thread Class Methods ----
  32. struct PlatformThreadData
  33. {
  34. ThreadRunFunction mRunFunc;
  35. void* mRunArg;
  36. Thread* mThread;
  37. Semaphore mGateway; // default count is 1
  38. ThreadIdent mThreadID;
  39. };
  40. //-----------------------------------------------------------------------------
  41. static void *ThreadRunHandler( void* arg )
  42. {
  43. // Fetch the platform thread data.
  44. PlatformThreadData* mData = reinterpret_cast<PlatformThreadData*>(arg);
  45. // Fetch the thread.
  46. Thread* thread = mData->mThread;
  47. mData->mThreadID = ThreadManager::getCurrentThreadId();
  48. // Add the thread.
  49. ThreadManager::addThread(thread);
  50. // Run the thread against a pool.
  51. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  52. thread->run(mData->mRunArg);
  53. [pool drain];
  54. // Release the thread.
  55. mData->mGateway.release();
  56. // Does the thread want to be auto-deleted?
  57. if(thread->autoDelete)
  58. {
  59. // Yes, so remove the thread.
  60. ThreadManager::removeThread(thread);
  61. // Delete the thread.
  62. delete thread;
  63. }
  64. // This is for pthread.
  65. return NULL;
  66. }
  67. //-----------------------------------------------------------------------------
  68. Thread::Thread(ThreadRunFunction func, void* arg, bool start_thread, bool autodelete)
  69. {
  70. mData = new PlatformThreadData;
  71. mData->mRunFunc = func;
  72. mData->mRunArg = arg;
  73. mData->mThread = this;
  74. mData->mThreadID = 0;
  75. autoDelete = autodelete;
  76. if(start_thread)
  77. start();
  78. }
  79. //-----------------------------------------------------------------------------
  80. Thread::~Thread()
  81. {
  82. stop();
  83. join();
  84. SAFE_DELETE(mData);
  85. }
  86. //-----------------------------------------------------------------------------
  87. void Thread::start()
  88. {
  89. if(isAlive())
  90. return;
  91. // cause start to block out other pthreads from using this Thread,
  92. // at least until ThreadRunHandler exits.
  93. mData->mGateway.acquire();
  94. // reset the shouldStop flag, so we'll know when someone asks us to stop.
  95. shouldStop = false;
  96. pthread_create((pthread_t*)(&mData->mThreadID), NULL, ThreadRunHandler, mData);
  97. }
  98. //-----------------------------------------------------------------------------
  99. bool Thread::join()
  100. {
  101. if(!isAlive())
  102. return true;
  103. // not using pthread_join here because pthread_join cannot deal
  104. // with multiple simultaneous calls.
  105. mData->mGateway.acquire();
  106. mData->mGateway.release();
  107. return true;
  108. }
  109. //-----------------------------------------------------------------------------
  110. void Thread::run(void* arg)
  111. {
  112. if(mData->mRunFunc)
  113. mData->mRunFunc(arg);
  114. }
  115. //-----------------------------------------------------------------------------
  116. bool Thread::isAlive()
  117. {
  118. if(mData->mThreadID == 0)
  119. return false;
  120. if( mData->mGateway.acquire(false) )
  121. {
  122. mData->mGateway.release();
  123. return false; // we got the lock, it aint alive.
  124. }
  125. else
  126. return true; // we could not get the lock, it must be alive.
  127. }
  128. //-----------------------------------------------------------------------------
  129. ThreadIdent Thread::getId()
  130. {
  131. return mData->mThreadID;
  132. }
  133. #pragma mark ---- ThreadManager Class Methods ----
  134. //-----------------------------------------------------------------------------
  135. ThreadIdent ThreadManager::getCurrentThreadId()
  136. {
  137. return (ThreadIdent)pthread_self();
  138. }
  139. //-----------------------------------------------------------------------------
  140. bool ThreadManager::compare( ThreadIdent threadId_1, ThreadIdent threadId_2 )
  141. {
  142. return (bool)pthread_equal((pthread_t)threadId_1, (pthread_t)threadId_2);
  143. }
  144. class ExecuteThread : public Thread
  145. {
  146. const char* zargs;
  147. const char* directory;
  148. const char* executable;
  149. public:
  150. ExecuteThread(const char *_executable, const char *_args /* = NULL */, const char *_directory /* = NULL */) : Thread(0, NULL, false, true)
  151. {
  152. zargs = dStrdup(_args);
  153. directory = dStrdup(_directory);
  154. executable = dStrdup(_executable);
  155. start();
  156. }
  157. static U32 runNoThread(const char *_executable, const char *_args /* = NULL */, const char *_directory /* = NULL */);
  158. virtual void run(void* arg);
  159. };
  160. static char* _unDoubleQuote(char* arg)
  161. {
  162. U32 len = dStrlen(arg);
  163. if(!len)
  164. return arg;
  165. if(arg[0] == '"' && arg[len-1] == '"')
  166. {
  167. arg[len - 1] = '\0';
  168. return arg + 1;
  169. }
  170. return arg;
  171. }
  172. // this is an externably callable blocking shellExecute!!!
  173. U32 ExecuteThread::runNoThread( const char* executable, const char* zargs, const char* directory )
  174. {
  175. printf("creating nstask\n");
  176. NSTask *aTask = [[NSTask alloc] init];
  177. NSMutableArray *array = [NSMutableArray array];
  178. // scan the args list, breaking it up, space delimited, backslash escaped.
  179. U32 len = dStrlen(zargs);
  180. char args[len+1];
  181. dStrncpy(args, zargs, len+1);
  182. char *lastarg = args;
  183. bool escaping = false;
  184. for(int i = 0; i< len; i++)
  185. {
  186. char c = args[i];
  187. // a backslash escapes the next character
  188. if(escaping)
  189. continue;
  190. if(c == '\\')
  191. escaping = true;
  192. if(c == ' ')
  193. {
  194. args[i] = '\0';
  195. if(*lastarg)
  196. [array addObject:[NSString stringWithUTF8String: _unDoubleQuote(lastarg)]];
  197. lastarg = args + i + 1;
  198. }
  199. }
  200. if(*lastarg)
  201. [array addObject:[NSString stringWithUTF8String: _unDoubleQuote(lastarg)]];
  202. [aTask setArguments: array];
  203. [aTask setCurrentDirectoryPath:[NSString stringWithUTF8String: directory]];
  204. [aTask setLaunchPath:[NSString stringWithUTF8String:executable]];
  205. [aTask launch];
  206. [aTask waitUntilExit];
  207. U32 ret = [aTask terminationStatus];
  208. return ret;
  209. }
  210. void ExecuteThread::run(void* arg)
  211. {
  212. // call the common run, but since we're in a thread, this won't block other processes
  213. U32 ret = runNoThread( this->executable, this->zargs, this->directory );
  214. Con::executef(2, "onExecuteDone", Con::getIntArg(ret));
  215. printf("done nstask\n");
  216. }
  217. ConsoleFunction(shellExecute, bool, 2, 4, "(executable, [args], [directory])")
  218. {
  219. ExecuteThread *et;
  220. et = new ExecuteThread(argv[1], argc > 2 ? argv[2] : NULL, argc > 3 ? argv[3] : NULL);
  221. return true; // Bug: BPNC error: need feedback on whether the command was sucessful
  222. }
  223. ConsoleFunction(shellExecuteBlocking, int, 2, 4, "(executable, [args], [directory])")
  224. {
  225. return (int)ExecuteThread::runNoThread( argv[1], argc > 2 ? argv[2] : NULL, argc > 3 ? argv[3] : NULL );
  226. }