objc-initialize.mm 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. /*
  2. * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved.
  3. *
  4. * @APPLE_LICENSE_HEADER_START@
  5. *
  6. * This file contains Original Code and/or Modifications of Original Code
  7. * as defined in and that are subject to the Apple Public Source License
  8. * Version 2.0 (the 'License'). You may not use this file except in
  9. * compliance with the License. Please obtain a copy of the License at
  10. * http://www.opensource.apple.com/apsl/ and read it before using this
  11. * file.
  12. *
  13. * The Original Code and all software distributed under the License are
  14. * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  15. * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  16. * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  18. * Please see the License for the specific language governing rights and
  19. * limitations under the License.
  20. *
  21. * @APPLE_LICENSE_HEADER_END@
  22. */
  23. /***********************************************************************
  24. * objc-initialize.m
  25. * +initialize support
  26. **********************************************************************/
  27. /***********************************************************************
  28. * Thread-safety during class initialization (GrP 2001-9-24)
  29. *
  30. * Initial state: CLS_INITIALIZING and CLS_INITIALIZED both clear.
  31. * During initialization: CLS_INITIALIZING is set
  32. * After initialization: CLS_INITIALIZING clear and CLS_INITIALIZED set.
  33. * CLS_INITIALIZING and CLS_INITIALIZED are never set at the same time.
  34. * CLS_INITIALIZED is never cleared once set.
  35. *
  36. * Only one thread is allowed to actually initialize a class and send
  37. * +initialize. Enforced by allowing only one thread to set CLS_INITIALIZING.
  38. *
  39. * Additionally, threads trying to send messages to a class must wait for
  40. * +initialize to finish. During initialization of a class, that class's
  41. * method cache is kept empty. objc_msgSend will revert to
  42. * class_lookupMethodAndLoadCache, which checks CLS_INITIALIZED before
  43. * messaging. If CLS_INITIALIZED is clear but CLS_INITIALIZING is set,
  44. * the thread must block, unless it is the thread that started
  45. * initializing the class in the first place.
  46. *
  47. * Each thread keeps a list of classes it's initializing.
  48. * The global classInitLock is used to synchronize changes to CLS_INITIALIZED
  49. * and CLS_INITIALIZING: the transition to CLS_INITIALIZING must be
  50. * an atomic test-and-set with respect to itself and the transition
  51. * to CLS_INITIALIZED.
  52. * The global classInitWaitCond is used to block threads waiting for an
  53. * initialization to complete. The classInitLock synchronizes
  54. * condition checking and the condition variable.
  55. **********************************************************************/
  56. /***********************************************************************
  57. * +initialize deadlock case when a class is marked initializing while
  58. * its superclass is initialized. Solved by completely initializing
  59. * superclasses before beginning to initialize a class.
  60. *
  61. * OmniWeb class hierarchy:
  62. * OBObject
  63. * | ` OBPostLoader
  64. * OFObject
  65. * / \
  66. * OWAddressEntry OWController
  67. * |
  68. * OWConsoleController
  69. *
  70. * Thread 1 (evil testing thread):
  71. * initialize OWAddressEntry
  72. * super init OFObject
  73. * super init OBObject
  74. * [OBObject initialize] runs OBPostLoader, which inits lots of classes...
  75. * initialize OWConsoleController
  76. * super init OWController - wait for Thread 2 to finish OWController init
  77. *
  78. * Thread 2 (normal OmniWeb thread):
  79. * initialize OWController
  80. * super init OFObject - wait for Thread 1 to finish OFObject init
  81. *
  82. * deadlock!
  83. *
  84. * Solution: fully initialize super classes before beginning to initialize
  85. * a subclass. Then the initializing+initialized part of the class hierarchy
  86. * will be a contiguous subtree starting at the root, so other threads
  87. * can't jump into the middle between two initializing classes, and we won't
  88. * get stuck while a superclass waits for its subclass which waits for the
  89. * superclass.
  90. **********************************************************************/
  91. #include "objc-private.h"
  92. #include "message.h"
  93. #include "objc-initialize.h"
  94. /* classInitLock protects CLS_INITIALIZED and CLS_INITIALIZING, and
  95. * is signalled when any class is done initializing.
  96. * Threads that are waiting for a class to finish initializing wait on this. */
  97. monitor_t classInitLock;
  98. /***********************************************************************
  99. * struct _objc_initializing_classes
  100. * Per-thread list of classes currently being initialized by that thread.
  101. * During initialization, that thread is allowed to send messages to that
  102. * class, but other threads have to wait.
  103. * The list is a simple array of metaclasses (the metaclass stores
  104. * the initialization state).
  105. **********************************************************************/
  106. typedef struct _objc_initializing_classes {
  107. int classesAllocated;
  108. Class *metaclasses;
  109. } _objc_initializing_classes;
  110. /***********************************************************************
  111. * _fetchInitializingClassList
  112. * Return the list of classes being initialized by this thread.
  113. * If create == YES, create the list when no classes are being initialized by this thread.
  114. * If create == NO, return nil when no classes are being initialized by this thread.
  115. **********************************************************************/
  116. static _objc_initializing_classes *_fetchInitializingClassList(bool create)
  117. {
  118. _objc_pthread_data *data;
  119. _objc_initializing_classes *list;
  120. Class *classes;
  121. data = _objc_fetch_pthread_data(create);
  122. if (data == nil) return nil;
  123. list = data->initializingClasses;
  124. if (list == nil) {
  125. if (!create) {
  126. return nil;
  127. } else {
  128. list = (_objc_initializing_classes *)
  129. calloc(1, sizeof(_objc_initializing_classes));
  130. data->initializingClasses = list;
  131. }
  132. }
  133. classes = list->metaclasses;
  134. if (classes == nil) {
  135. // If _objc_initializing_classes exists, allocate metaclass array,
  136. // even if create == NO.
  137. // Allow 4 simultaneous class inits on this thread before realloc.
  138. list->classesAllocated = 4;
  139. classes = (Class *)
  140. calloc(list->classesAllocated, sizeof(Class));
  141. list->metaclasses = classes;
  142. }
  143. return list;
  144. }
  145. /***********************************************************************
  146. * _destroyInitializingClassList
  147. * Deallocate memory used by the given initialization list.
  148. * Any part of the list may be nil.
  149. * Called from _objc_pthread_destroyspecific().
  150. **********************************************************************/
  151. void _destroyInitializingClassList(struct _objc_initializing_classes *list)
  152. {
  153. if (list != nil) {
  154. if (list->metaclasses != nil) {
  155. free(list->metaclasses);
  156. }
  157. free(list);
  158. }
  159. }
  160. /***********************************************************************
  161. * _thisThreadIsInitializingClass
  162. * Return TRUE if this thread is currently initializing the given class.
  163. **********************************************************************/
  164. bool _thisThreadIsInitializingClass(Class cls)
  165. {
  166. int i;
  167. _objc_initializing_classes *list = _fetchInitializingClassList(NO);
  168. if (list) {
  169. cls = cls->getMeta();
  170. for (i = 0; i < list->classesAllocated; i++) {
  171. if (cls == list->metaclasses[i]) return YES;
  172. }
  173. }
  174. // no list or not found in list
  175. return NO;
  176. }
  177. /***********************************************************************
  178. * _setThisThreadIsInitializingClass
  179. * Record that this thread is currently initializing the given class.
  180. * This thread will be allowed to send messages to the class, but
  181. * other threads will have to wait.
  182. **********************************************************************/
  183. static void _setThisThreadIsInitializingClass(Class cls)
  184. {
  185. int i;
  186. _objc_initializing_classes *list = _fetchInitializingClassList(YES);
  187. cls = cls->getMeta();
  188. // paranoia: explicitly disallow duplicates
  189. for (i = 0; i < list->classesAllocated; i++) {
  190. if (cls == list->metaclasses[i]) {
  191. _objc_fatal("thread is already initializing this class!");
  192. return; // already the initializer
  193. }
  194. }
  195. for (i = 0; i < list->classesAllocated; i++) {
  196. if (! list->metaclasses[i]) {
  197. list->metaclasses[i] = cls;
  198. return;
  199. }
  200. }
  201. // class list is full - reallocate
  202. list->classesAllocated = list->classesAllocated * 2 + 1;
  203. list->metaclasses = (Class *)
  204. realloc(list->metaclasses,
  205. list->classesAllocated * sizeof(Class));
  206. // zero out the new entries
  207. list->metaclasses[i++] = cls;
  208. for ( ; i < list->classesAllocated; i++) {
  209. list->metaclasses[i] = nil;
  210. }
  211. }
  212. /***********************************************************************
  213. * _setThisThreadIsNotInitializingClass
  214. * Record that this thread is no longer initializing the given class.
  215. **********************************************************************/
  216. static void _setThisThreadIsNotInitializingClass(Class cls)
  217. {
  218. int i;
  219. _objc_initializing_classes *list = _fetchInitializingClassList(NO);
  220. if (list) {
  221. cls = cls->getMeta();
  222. for (i = 0; i < list->classesAllocated; i++) {
  223. if (cls == list->metaclasses[i]) {
  224. list->metaclasses[i] = nil;
  225. return;
  226. }
  227. }
  228. }
  229. // no list or not found in list
  230. _objc_fatal("thread is not initializing this class!");
  231. }
  232. typedef struct PendingInitialize {
  233. Class subclass;
  234. struct PendingInitialize *next;
  235. } PendingInitialize;
  236. static NXMapTable *pendingInitializeMap;
  237. /***********************************************************************
  238. * _finishInitializing
  239. * cls has completed its +initialize method, and so has its superclass.
  240. * Mark cls as initialized as well, then mark any of cls's subclasses
  241. * that have already finished their own +initialize methods.
  242. **********************************************************************/
  243. static void _finishInitializing(Class cls, Class supercls)
  244. {
  245. PendingInitialize *pending;
  246. classInitLock.assertLocked();
  247. assert(!supercls || supercls->isInitialized());
  248. if (PrintInitializing) {
  249. _objc_inform("INITIALIZE: thread %p: %s is fully +initialized",
  250. pthread_self(), cls->nameForLogging());
  251. }
  252. // mark this class as fully +initialized
  253. cls->setInitialized();
  254. classInitLock.notifyAll();
  255. _setThisThreadIsNotInitializingClass(cls);
  256. // mark any subclasses that were merely waiting for this class
  257. if (!pendingInitializeMap) return;
  258. pending = (PendingInitialize *)NXMapGet(pendingInitializeMap, cls);
  259. if (!pending) return;
  260. NXMapRemove(pendingInitializeMap, cls);
  261. // Destroy the pending table if it's now empty, to save memory.
  262. if (NXCountMapTable(pendingInitializeMap) == 0) {
  263. NXFreeMapTable(pendingInitializeMap);
  264. pendingInitializeMap = nil;
  265. }
  266. while (pending) {
  267. PendingInitialize *next = pending->next;
  268. if (pending->subclass) _finishInitializing(pending->subclass, cls);
  269. free(pending);
  270. pending = next;
  271. }
  272. }
  273. /***********************************************************************
  274. * _finishInitializingAfter
  275. * cls has completed its +initialize method, but its superclass has not.
  276. * Wait until supercls finishes before marking cls as initialized.
  277. **********************************************************************/
  278. static void _finishInitializingAfter(Class cls, Class supercls)
  279. {
  280. PendingInitialize *pending;
  281. classInitLock.assertLocked();
  282. if (PrintInitializing) {
  283. _objc_inform("INITIALIZE: thread %p: class %s will be marked as fully "
  284. "+initialized after superclass +[%s initialize] completes",
  285. pthread_self(), cls->nameForLogging(),
  286. supercls->nameForLogging());
  287. }
  288. if (!pendingInitializeMap) {
  289. pendingInitializeMap =
  290. NXCreateMapTable(NXPtrValueMapPrototype, 10);
  291. // fixme pre-size this table for CF/NSObject +initialize
  292. }
  293. pending = (PendingInitialize *)malloc(sizeof(*pending));
  294. pending->subclass = cls;
  295. pending->next = (PendingInitialize *)
  296. NXMapGet(pendingInitializeMap, supercls);
  297. NXMapInsert(pendingInitializeMap, supercls, pending);
  298. }
  299. // Provide helpful messages in stack traces.
  300. OBJC_EXTERN __attribute__((noinline, used, visibility("hidden")))
  301. void waitForInitializeToComplete(Class cls)
  302. asm("_WAITING_FOR_ANOTHER_THREAD_TO_FINISH_CALLING_+initialize");
  303. OBJC_EXTERN __attribute__((noinline, used, visibility("hidden")))
  304. void callInitialize(Class cls)
  305. asm("_CALLING_SOME_+initialize_METHOD");
  306. void waitForInitializeToComplete(Class cls)
  307. {
  308. if (PrintInitializing) {
  309. _objc_inform("INITIALIZE: thread %p: blocking until +[%s initialize] "
  310. "completes", pthread_self(), cls->nameForLogging());
  311. }
  312. monitor_locker_t lock(classInitLock);
  313. while (!cls->isInitialized()) {
  314. classInitLock.wait();
  315. }
  316. asm("");
  317. }
  318. void callInitialize(Class cls)
  319. {
  320. ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
  321. asm("");
  322. }
  323. /***********************************************************************
  324. * classHasTrivialInitialize
  325. * Returns true if the class has no +initialize implementation or
  326. * has a +initialize implementation that looks empty.
  327. * Any root class +initialize implemetation is assumed to be trivial.
  328. **********************************************************************/
  329. static bool classHasTrivialInitialize(Class cls)
  330. {
  331. if (cls->isRootClass() || cls->isRootMetaclass()) return true;
  332. Class rootCls = cls->ISA()->ISA()->superclass;
  333. IMP rootImp = lookUpImpOrNil(rootCls->ISA(), SEL_initialize, rootCls,
  334. NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
  335. IMP imp = lookUpImpOrNil(cls->ISA(), SEL_initialize, cls,
  336. NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
  337. return (imp == nil || imp == (IMP)&objc_noop_imp || imp == rootImp);
  338. }
  339. /***********************************************************************
  340. * lockAndFinishInitializing
  341. * Mark a class as finished initializing and notify waiters, or queue for later.
  342. * If the superclass is also done initializing, then update
  343. * the info bits and notify waiting threads.
  344. * If not, update them later. (This can happen if this +initialize
  345. * was itself triggered from inside a superclass +initialize.)
  346. **********************************************************************/
  347. static void lockAndFinishInitializing(Class cls, Class supercls)
  348. {
  349. monitor_locker_t lock(classInitLock);
  350. if (!supercls || supercls->isInitialized()) {
  351. _finishInitializing(cls, supercls);
  352. } else {
  353. _finishInitializingAfter(cls, supercls);
  354. }
  355. }
  356. /***********************************************************************
  357. * performForkChildInitialize
  358. * +initialize after fork() is problematic. It's possible for the
  359. * fork child process to call some +initialize that would deadlock waiting
  360. * for another +initialize in the parent process.
  361. * We wouldn't know how much progress it made therein, so we can't
  362. * act as if +initialize completed nor can we restart +initialize
  363. * from scratch.
  364. *
  365. * Instead we proceed introspectively. If the class has some
  366. * +initialize implementation, we halt. If the class has no
  367. * +initialize implementation of its own, we continue. Root
  368. * class +initialize is assumed to be empty if it exists.
  369. *
  370. * We apply this rule even if the child's +initialize does not appear
  371. * to be blocked by anything. This prevents races wherein the +initialize
  372. * deadlock only rarely hits. Instead we disallow it even when we "won"
  373. * the race.
  374. *
  375. * Exception: processes that are single-threaded when fork() is called
  376. * have no restrictions on +initialize in the child. Examples: sshd and httpd.
  377. *
  378. * Classes that wish to implement +initialize and be callable after
  379. * fork() must use an atfork() handler to provoke +initialize in fork prepare.
  380. **********************************************************************/
  381. // Called before halting when some +initialize
  382. // method can't be called after fork().
  383. BREAKPOINT_FUNCTION(
  384. void objc_initializeAfterForkError(Class cls)
  385. );
  386. void performForkChildInitialize(Class cls, Class supercls)
  387. {
  388. if (classHasTrivialInitialize(cls)) {
  389. if (PrintInitializing) {
  390. _objc_inform("INITIALIZE: thread %p: skipping trivial +[%s "
  391. "initialize] in fork() child process",
  392. pthread_self(), cls->nameForLogging());
  393. }
  394. lockAndFinishInitializing(cls, supercls);
  395. }
  396. else {
  397. if (PrintInitializing) {
  398. _objc_inform("INITIALIZE: thread %p: refusing to call +[%s "
  399. "initialize] in fork() child process because "
  400. "it may have been in progress when fork() was called",
  401. pthread_self(), cls->nameForLogging());
  402. }
  403. _objc_inform_now_and_on_crash
  404. ("+[%s initialize] may have been in progress in another thread "
  405. "when fork() was called.",
  406. cls->nameForLogging());
  407. objc_initializeAfterForkError(cls);
  408. _objc_fatal
  409. ("+[%s initialize] may have been in progress in another thread "
  410. "when fork() was called. We cannot safely call it or "
  411. "ignore it in the fork() child process. Crashing instead. "
  412. "Set a breakpoint on objc_initializeAfterForkError to debug.",
  413. cls->nameForLogging());
  414. }
  415. }
  416. /***********************************************************************
  417. * class_initialize. Send the '+initialize' message on demand to any
  418. * uninitialized class. Force initialization of superclasses first.
  419. **********************************************************************/
  420. void _class_initialize(Class cls)
  421. {
  422. assert(!cls->isMetaClass());
  423. Class supercls;
  424. bool reallyInitialize = NO;
  425. // Make sure super is done initializing BEFORE beginning to initialize cls.
  426. // See note about deadlock above.
  427. supercls = cls->superclass;
  428. if (supercls && !supercls->isInitialized()) {
  429. _class_initialize(supercls);
  430. }
  431. // Try to atomically set CLS_INITIALIZING.
  432. {
  433. monitor_locker_t lock(classInitLock);
  434. if (!cls->isInitialized() && !cls->isInitializing()) {
  435. cls->setInitializing();
  436. reallyInitialize = YES;
  437. }
  438. }
  439. if (reallyInitialize) {
  440. // We successfully set the CLS_INITIALIZING bit. Initialize the class.
  441. // Record that we're initializing this class so we can message it.
  442. _setThisThreadIsInitializingClass(cls);
  443. if (MultithreadedForkChild) {
  444. // LOL JK we don't really call +initialize methods after fork().
  445. performForkChildInitialize(cls, supercls);
  446. return;
  447. }
  448. // Send the +initialize message.
  449. // Note that +initialize is sent to the superclass (again) if
  450. // this class doesn't implement +initialize. 2157218
  451. if (PrintInitializing) {
  452. _objc_inform("INITIALIZE: thread %p: calling +[%s initialize]",
  453. pthread_self(), cls->nameForLogging());
  454. }
  455. // Exceptions: A +initialize call that throws an exception
  456. // is deemed to be a complete and successful +initialize.
  457. //
  458. // Only __OBJC2__ adds these handlers. !__OBJC2__ has a
  459. // bootstrapping problem of this versus CF's call to
  460. // objc_exception_set_functions().
  461. #if __OBJC2__
  462. @try
  463. #endif
  464. {
  465. callInitialize(cls);
  466. if (PrintInitializing) {
  467. _objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
  468. pthread_self(), cls->nameForLogging());
  469. }
  470. }
  471. #if __OBJC2__
  472. @catch (...) {
  473. if (PrintInitializing) {
  474. _objc_inform("INITIALIZE: thread %p: +[%s initialize] "
  475. "threw an exception",
  476. pthread_self(), cls->nameForLogging());
  477. }
  478. @throw;
  479. }
  480. @finally
  481. #endif
  482. {
  483. // Done initializing.
  484. lockAndFinishInitializing(cls, supercls);
  485. }
  486. return;
  487. }
  488. else if (cls->isInitializing()) {
  489. // We couldn't set INITIALIZING because INITIALIZING was already set.
  490. // If this thread set it earlier, continue normally.
  491. // If some other thread set it, block until initialize is done.
  492. // It's ok if INITIALIZING changes to INITIALIZED while we're here,
  493. // because we safely check for INITIALIZED inside the lock
  494. // before blocking.
  495. if (_thisThreadIsInitializingClass(cls)) {
  496. return;
  497. } else if (!MultithreadedForkChild) {
  498. waitForInitializeToComplete(cls);
  499. return;
  500. } else {
  501. // We're on the child side of fork(), facing a class that
  502. // was initializing by some other thread when fork() was called.
  503. _setThisThreadIsInitializingClass(cls);
  504. performForkChildInitialize(cls, supercls);
  505. }
  506. }
  507. else if (cls->isInitialized()) {
  508. // Set CLS_INITIALIZING failed because someone else already
  509. // initialized the class. Continue normally.
  510. // NOTE this check must come AFTER the ISINITIALIZING case.
  511. // Otherwise: Another thread is initializing this class. ISINITIALIZED
  512. // is false. Skip this clause. Then the other thread finishes
  513. // initialization and sets INITIALIZING=no and INITIALIZED=yes.
  514. // Skip the ISINITIALIZING clause. Die horribly.
  515. return;
  516. }
  517. else {
  518. // We shouldn't be here.
  519. _objc_fatal("thread-safe class init in objc runtime is buggy!");
  520. }
  521. }