1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383 |
- /*
- * Copyright (c) 2010-2012 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
- #include "objc-private.h"
- #include "NSObject.h"
- #include "objc-weak.h"
- #include "DenseMapExtras.h"
- #include <malloc/malloc.h>
- #include <stdint.h>
- #include <stdbool.h>
- #include <mach/mach.h>
- #include <mach-o/dyld.h>
- #include <mach-o/nlist.h>
- #include <sys/types.h>
- #include <sys/mman.h>
- #include <Block.h>
- #include <map>
- #include <execinfo.h>
- #include "NSObject-internal.h"
- @interface NSInvocation
- - (SEL)selector;
- @end
- OBJC_EXTERN const uint32_t objc_debug_autoreleasepoolpage_magic_offset = __builtin_offsetof(AutoreleasePoolPageData, magic);
- OBJC_EXTERN const uint32_t objc_debug_autoreleasepoolpage_next_offset = __builtin_offsetof(AutoreleasePoolPageData, next);
- OBJC_EXTERN const uint32_t objc_debug_autoreleasepoolpage_thread_offset = __builtin_offsetof(AutoreleasePoolPageData, thread);
- OBJC_EXTERN const uint32_t objc_debug_autoreleasepoolpage_parent_offset = __builtin_offsetof(AutoreleasePoolPageData, parent);
- OBJC_EXTERN const uint32_t objc_debug_autoreleasepoolpage_child_offset = __builtin_offsetof(AutoreleasePoolPageData, child);
- OBJC_EXTERN const uint32_t objc_debug_autoreleasepoolpage_depth_offset = __builtin_offsetof(AutoreleasePoolPageData, depth);
- OBJC_EXTERN const uint32_t objc_debug_autoreleasepoolpage_hiwat_offset = __builtin_offsetof(AutoreleasePoolPageData, hiwat);
- /***********************************************************************
- * Weak ivar support
- **********************************************************************/
- static id defaultBadAllocHandler(Class cls)
- {
- _objc_fatal("attempt to allocate object of class '%s' failed",
- cls->nameForLogging());
- }
- id(*badAllocHandler)(Class) = &defaultBadAllocHandler;
- id _objc_callBadAllocHandler(Class cls)
- {
- // fixme add re-entrancy protection in case allocation fails inside handler
- return (*badAllocHandler)(cls);
- }
- void _objc_setBadAllocHandler(id(*newHandler)(Class))
- {
- badAllocHandler = newHandler;
- }
- namespace {
- // The order of these bits is important.
- #define SIDE_TABLE_WEAKLY_REFERENCED (1UL<<0)
- #define SIDE_TABLE_DEALLOCATING (1UL<<1) // MSB-ward of weak bit
- #define SIDE_TABLE_RC_ONE (1UL<<2) // MSB-ward of deallocating bit
- #define SIDE_TABLE_RC_PINNED (1UL<<(WORD_BITS-1))
- #define SIDE_TABLE_RC_SHIFT 2
- #define SIDE_TABLE_FLAG_MASK (SIDE_TABLE_RC_ONE-1)
- struct RefcountMapValuePurgeable {
- static inline bool isPurgeable(size_t x) {
- return x == 0;
- }
- };
- // RefcountMap disguises its pointers because we
- // don't want the table to act as a root for `leaks`.
- typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable> RefcountMap;
- // Template parameters.
- enum HaveOld { DontHaveOld = false, DoHaveOld = true };
- enum HaveNew { DontHaveNew = false, DoHaveNew = true };
- struct SideTable {
- spinlock_t slock;
- RefcountMap refcnts;
- weak_table_t weak_table;
- SideTable() {
- memset(&weak_table, 0, sizeof(weak_table));
- }
- ~SideTable() {
- _objc_fatal("Do not delete SideTable.");
- }
- void lock() { slock.lock(); }
- void unlock() { slock.unlock(); }
- void forceReset() { slock.forceReset(); }
- // Address-ordered lock discipline for a pair of side tables.
- template<HaveOld, HaveNew>
- static void lockTwo(SideTable *lock1, SideTable *lock2);
- template<HaveOld, HaveNew>
- static void unlockTwo(SideTable *lock1, SideTable *lock2);
- };
- template<>
- void SideTable::lockTwo<DoHaveOld, DoHaveNew>
- (SideTable *lock1, SideTable *lock2)
- {
- spinlock_t::lockTwo(&lock1->slock, &lock2->slock);
- }
- template<>
- void SideTable::lockTwo<DoHaveOld, DontHaveNew>
- (SideTable *lock1, SideTable *)
- {
- lock1->lock();
- }
- template<>
- void SideTable::lockTwo<DontHaveOld, DoHaveNew>
- (SideTable *, SideTable *lock2)
- {
- lock2->lock();
- }
- template<>
- void SideTable::unlockTwo<DoHaveOld, DoHaveNew>
- (SideTable *lock1, SideTable *lock2)
- {
- spinlock_t::unlockTwo(&lock1->slock, &lock2->slock);
- }
- template<>
- void SideTable::unlockTwo<DoHaveOld, DontHaveNew>
- (SideTable *lock1, SideTable *)
- {
- lock1->unlock();
- }
- template<>
- void SideTable::unlockTwo<DontHaveOld, DoHaveNew>
- (SideTable *, SideTable *lock2)
- {
- lock2->unlock();
- }
- static objc::ExplicitInit<StripedMap<SideTable>> SideTablesMap;
- static StripedMap<SideTable>& SideTables() {
- return SideTablesMap.get();
- }
- // anonymous namespace
- };
- void SideTableLockAll() {
- SideTables().lockAll();
- }
- void SideTableUnlockAll() {
- SideTables().unlockAll();
- }
- void SideTableForceResetAll() {
- SideTables().forceResetAll();
- }
- void SideTableDefineLockOrder() {
- SideTables().defineLockOrder();
- }
- void SideTableLocksPrecedeLock(const void *newlock) {
- SideTables().precedeLock(newlock);
- }
- void SideTableLocksSucceedLock(const void *oldlock) {
- SideTables().succeedLock(oldlock);
- }
- void SideTableLocksPrecedeLocks(StripedMap<spinlock_t>& newlocks) {
- int i = 0;
- const void *newlock;
- while ((newlock = newlocks.getLock(i++))) {
- SideTables().precedeLock(newlock);
- }
- }
- void SideTableLocksSucceedLocks(StripedMap<spinlock_t>& oldlocks) {
- int i = 0;
- const void *oldlock;
- while ((oldlock = oldlocks.getLock(i++))) {
- SideTables().succeedLock(oldlock);
- }
- }
- //
- // The -fobjc-arc flag causes the compiler to issue calls to objc_{retain/release/autorelease/retain_block}
- //
- id objc_retainBlock(id x) {
- return (id)_Block_copy(x);
- }
- //
- // The following SHOULD be called by the compiler directly, but the request hasn't been made yet :-)
- //
- BOOL objc_should_deallocate(id object) {
- return YES;
- }
- id
- objc_retain_autorelease(id obj)
- {
- return objc_autorelease(objc_retain(obj));
- }
- void
- objc_storeStrong(id *location, id obj)
- {
- id prev = *location;
- if (obj == prev) {
- return;
- }
- objc_retain(obj);
- *location = obj;
- objc_release(prev);
- }
- // Update a weak variable.
- // If HaveOld is true, the variable has an existing value
- // that needs to be cleaned up. This value might be nil.
- // If HaveNew is true, there is a new value that needs to be
- // assigned into the variable. This value might be nil.
- // If CrashIfDeallocating is true, the process is halted if newObj is
- // deallocating or newObj's class does not support weak references.
- // If CrashIfDeallocating is false, nil is stored instead.
- enum CrashIfDeallocating {
- DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
- };
- template <HaveOld haveOld, HaveNew haveNew,
- CrashIfDeallocating crashIfDeallocating>
- static id
- storeWeak(id *location, objc_object *newObj)
- {
- ASSERT(haveOld || haveNew);
- if (!haveNew) ASSERT(newObj == nil);
- Class previouslyInitializedClass = nil;
- id oldObj;
- SideTable *oldTable;
- SideTable *newTable;
- // Acquire locks for old and new values.
- // Order by lock address to prevent lock ordering problems.
- // Retry if the old value changes underneath us.
- retry:
- if (haveOld) {
- oldObj = *location;
- oldTable = &SideTables()[oldObj];
- } else {
- oldTable = nil;
- }
- if (haveNew) {
- newTable = &SideTables()[newObj];
- } else {
- newTable = nil;
- }
- SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
- if (haveOld && *location != oldObj) {
- SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
- goto retry;
- }
- // Prevent a deadlock between the weak reference machinery
- // and the +initialize machinery by ensuring that no
- // weakly-referenced object has an un-+initialized isa.
- if (haveNew && newObj) {
- Class cls = newObj->getIsa();
- if (cls != previouslyInitializedClass &&
- !((objc_class *)cls)->isInitialized())
- {
- SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
- class_initialize(cls, (id)newObj);
- // If this class is finished with +initialize then we're good.
- // If this class is still running +initialize on this thread
- // (i.e. +initialize called storeWeak on an instance of itself)
- // then we may proceed but it will appear initializing and
- // not yet initialized to the check above.
- // Instead set previouslyInitializedClass to recognize it on retry.
- previouslyInitializedClass = cls;
- goto retry;
- }
- }
- // Clean up old value, if any.
- if (haveOld) {
- weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
- }
- // Assign new value, if any.
- if (haveNew) {
- newObj = (objc_object *)
- weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
- crashIfDeallocating);
- // weak_register_no_lock returns nil if weak store should be rejected
- // Set is-weakly-referenced bit in refcount table.
- if (newObj && !newObj->isTaggedPointer()) {
- newObj->setWeaklyReferenced_nolock();
- }
- // Do not set *location anywhere else. That would introduce a race.
- *location = (id)newObj;
- }
- else {
- // No new value. The storage is not changed.
- }
-
- SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
- return (id)newObj;
- }
- /**
- * This function stores a new value into a __weak variable. It would
- * be used anywhere a __weak variable is the target of an assignment.
- *
- * @param location The address of the weak pointer itself
- * @param newObj The new object this weak ptr should now point to
- *
- * @return \e newObj
- */
- id
- objc_storeWeak(id *location, id newObj)
- {
- return storeWeak<DoHaveOld, DoHaveNew, DoCrashIfDeallocating>
- (location, (objc_object *)newObj);
- }
- /**
- * This function stores a new value into a __weak variable.
- * If the new object is deallocating or the new object's class
- * does not support weak references, stores nil instead.
- *
- * @param location The address of the weak pointer itself
- * @param newObj The new object this weak ptr should now point to
- *
- * @return The value stored (either the new object or nil)
- */
- id
- objc_storeWeakOrNil(id *location, id newObj)
- {
- return storeWeak<DoHaveOld, DoHaveNew, DontCrashIfDeallocating>
- (location, (objc_object *)newObj);
- }
- /**
- * Initialize a fresh weak pointer to some object location.
- * It would be used for code like:
- *
- * (The nil case)
- * __weak id weakPtr;
- * (The non-nil case)
- * NSObject *o = ...;
- * __weak id weakPtr = o;
- *
- * This function IS NOT thread-safe with respect to concurrent
- * modifications to the weak variable. (Concurrent weak clear is safe.)
- *
- * @param location Address of __weak ptr.
- * @param newObj Object ptr.
- */
- id
- objc_initWeak(id *location, id newObj)
- {
- if (!newObj) {
- *location = nil;
- return nil;
- }
- return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
- (location, (objc_object*)newObj);
- }
- id
- objc_initWeakOrNil(id *location, id newObj)
- {
- if (!newObj) {
- *location = nil;
- return nil;
- }
- return storeWeak<DontHaveOld, DoHaveNew, DontCrashIfDeallocating>
- (location, (objc_object*)newObj);
- }
- /**
- * Destroys the relationship between a weak pointer
- * and the object it is referencing in the internal weak
- * table. If the weak pointer is not referencing anything,
- * there is no need to edit the weak table.
- *
- * This function IS NOT thread-safe with respect to concurrent
- * modifications to the weak variable. (Concurrent weak clear is safe.)
- *
- * @param location The weak pointer address.
- */
- void
- objc_destroyWeak(id *location)
- {
- (void)storeWeak<DoHaveOld, DontHaveNew, DontCrashIfDeallocating>
- (location, nil);
- }
- /*
- Once upon a time we eagerly cleared *location if we saw the object
- was deallocating. This confuses code like NSPointerFunctions which
- tries to pre-flight the raw storage and assumes if the storage is
- zero then the weak system is done interfering. That is false: the
- weak system is still going to check and clear the storage later.
- This can cause objc_weak_error complaints and crashes.
- So we now don't touch the storage until deallocation completes.
- */
- id
- objc_loadWeakRetained(id *location)
- {
- id obj;
- id result;
- Class cls;
- SideTable *table;
-
- retry:
- // fixme std::atomic this load
- obj = *location;
- if (!obj) return nil;
- if (obj->isTaggedPointer()) return obj;
-
- table = &SideTables()[obj];
-
- table->lock();
- if (*location != obj) {
- table->unlock();
- goto retry;
- }
-
- result = obj;
- cls = obj->ISA();
- if (! cls->hasCustomRR()) {
- // Fast case. We know +initialize is complete because
- // default-RR can never be set before then.
- ASSERT(cls->isInitialized());
- if (! obj->rootTryRetain()) {
- result = nil;
- }
- }
- else {
- // Slow case. We must check for +initialize and call it outside
- // the lock if necessary in order to avoid deadlocks.
- if (cls->isInitialized() || _thisThreadIsInitializingClass(cls)) {
- BOOL (*tryRetain)(id, SEL) = (BOOL(*)(id, SEL))
- class_getMethodImplementation(cls, @selector(retainWeakReference));
- if ((IMP)tryRetain == _objc_msgForward) {
- result = nil;
- }
- else if (! (*tryRetain)(obj, @selector(retainWeakReference))) {
- result = nil;
- }
- }
- else {
- table->unlock();
- class_initialize(cls, obj);
- goto retry;
- }
- }
-
- table->unlock();
- return result;
- }
- /**
- * This loads the object referenced by a weak pointer and returns it, after
- * retaining and autoreleasing the object to ensure that it stays alive
- * long enough for the caller to use it. This function would be used
- * anywhere a __weak variable is used in an expression.
- *
- * @param location The weak pointer address
- *
- * @return The object pointed to by \e location, or \c nil if \e location is \c nil.
- */
- id
- objc_loadWeak(id *location)
- {
- if (!*location) return nil;
- return objc_autorelease(objc_loadWeakRetained(location));
- }
- /**
- * This function copies a weak pointer from one location to another,
- * when the destination doesn't already contain a weak pointer. It
- * would be used for code like:
- *
- * __weak id src = ...;
- * __weak id dst = src;
- *
- * This function IS NOT thread-safe with respect to concurrent
- * modifications to the destination variable. (Concurrent weak clear is safe.)
- *
- * @param dst The destination variable.
- * @param src The source variable.
- */
- void
- objc_copyWeak(id *dst, id *src)
- {
- id obj = objc_loadWeakRetained(src);
- objc_initWeak(dst, obj);
- objc_release(obj);
- }
- /**
- * Move a weak pointer from one location to another.
- * Before the move, the destination must be uninitialized.
- * After the move, the source is nil.
- *
- * This function IS NOT thread-safe with respect to concurrent
- * modifications to either weak variable. (Concurrent weak clear is safe.)
- *
- */
- void
- objc_moveWeak(id *dst, id *src)
- {
- objc_copyWeak(dst, src);
- objc_destroyWeak(src);
- *src = nil;
- }
- /***********************************************************************
- Autorelease pool implementation
- A thread's autorelease pool is a stack of pointers.
- Each pointer is either an object to release, or POOL_BOUNDARY which is
- an autorelease pool boundary.
- A pool token is a pointer to the POOL_BOUNDARY for that pool. When
- the pool is popped, every object hotter than the sentinel is released.
- The stack is divided into a doubly-linked list of pages. Pages are added
- and deleted as necessary.
- Thread-local storage points to the hot page, where newly autoreleased
- objects are stored.
- **********************************************************************/
- BREAKPOINT_FUNCTION(void objc_autoreleaseNoPool(id obj));
- BREAKPOINT_FUNCTION(void objc_autoreleasePoolInvalid(const void *token));
- class AutoreleasePoolPage : private AutoreleasePoolPageData
- {
- friend struct thread_data_t;
- public:
- static size_t const SIZE =
- #if PROTECT_AUTORELEASEPOOL
- PAGE_MAX_SIZE; // must be multiple of vm page size
- #else
- PAGE_MIN_SIZE; // size and alignment, power of 2
- #endif
-
- private:
- static pthread_key_t const key = AUTORELEASE_POOL_KEY;
- static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing
- static size_t const COUNT = SIZE / sizeof(id);
- // EMPTY_POOL_PLACEHOLDER is stored in TLS when exactly one pool is
- // pushed and it has never contained any objects. This saves memory
- // when the top level (i.e. libdispatch) pushes and pops pools but
- // never uses them.
- # define EMPTY_POOL_PLACEHOLDER ((id*)1)
- # define POOL_BOUNDARY nil
- // SIZE-sizeof(*this) bytes of contents follow
- static void * operator new(size_t size) {
- return malloc_zone_memalign(malloc_default_zone(), SIZE, SIZE);
- }
- static void operator delete(void * p) {
- return free(p);
- }
- inline void protect() {
- #if PROTECT_AUTORELEASEPOOL
- mprotect(this, SIZE, PROT_READ);
- check();
- #endif
- }
- inline void unprotect() {
- #if PROTECT_AUTORELEASEPOOL
- check();
- mprotect(this, SIZE, PROT_READ | PROT_WRITE);
- #endif
- }
- AutoreleasePoolPage(AutoreleasePoolPage *newParent) :
- AutoreleasePoolPageData(begin(),
- objc_thread_self(),
- newParent,
- newParent ? 1+newParent->depth : 0,
- newParent ? newParent->hiwat : 0)
- {
- if (parent) {
- parent->check();
- ASSERT(!parent->child);
- parent->unprotect();
- parent->child = this;
- parent->protect();
- }
- protect();
- }
- ~AutoreleasePoolPage()
- {
- check();
- unprotect();
- ASSERT(empty());
- // Not recursive: we don't want to blow out the stack
- // if a thread accumulates a stupendous amount of garbage
- ASSERT(!child);
- }
- template<typename Fn>
- void
- busted(Fn log) const
- {
- magic_t right;
- log("autorelease pool page %p corrupted\n"
- " magic 0x%08x 0x%08x 0x%08x 0x%08x\n"
- " should be 0x%08x 0x%08x 0x%08x 0x%08x\n"
- " pthread %p\n"
- " should be %p\n",
- this,
- magic.m[0], magic.m[1], magic.m[2], magic.m[3],
- right.m[0], right.m[1], right.m[2], right.m[3],
- this->thread, objc_thread_self());
- }
- __attribute__((noinline, cold, noreturn))
- void
- busted_die() const
- {
- busted(_objc_fatal);
- __builtin_unreachable();
- }
- inline void
- check(bool die = true) const
- {
- if (!magic.check() || thread != objc_thread_self()) {
- if (die) {
- busted_die();
- } else {
- busted(_objc_inform);
- }
- }
- }
- inline void
- fastcheck() const
- {
- #if CHECK_AUTORELEASEPOOL
- check();
- #else
- if (! magic.fastcheck()) {
- busted_die();
- }
- #endif
- }
- id * begin() {
- return (id *) ((uint8_t *)this+sizeof(*this));
- }
- id * end() {
- return (id *) ((uint8_t *)this+SIZE);
- }
- bool empty() {
- return next == begin();
- }
- bool full() {
- return next == end();
- }
- bool lessThanHalfFull() {
- return (next - begin() < (end() - begin()) / 2);
- }
- id *add(id obj)
- {
- ASSERT(!full());
- unprotect();
- id *ret = next; // faster than `return next-1` because of aliasing
- *next++ = obj;
- protect();
- return ret;
- }
- void releaseAll()
- {
- releaseUntil(begin());
- }
- void releaseUntil(id *stop)
- {
- // Not recursive: we don't want to blow out the stack
- // if a thread accumulates a stupendous amount of garbage
-
- while (this->next != stop) {
- // Restart from hotPage() every time, in case -release
- // autoreleased more objects
- AutoreleasePoolPage *page = hotPage();
- // fixme I think this `while` can be `if`, but I can't prove it
- while (page->empty()) {
- page = page->parent;
- setHotPage(page);
- }
- page->unprotect();
- id obj = *--page->next;
- memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
- page->protect();
- if (obj != POOL_BOUNDARY) {
- objc_release(obj);
- }
- }
- setHotPage(this);
- #if DEBUG
- // we expect any children to be completely empty
- for (AutoreleasePoolPage *page = child; page; page = page->child) {
- ASSERT(page->empty());
- }
- #endif
- }
- void kill()
- {
- // Not recursive: we don't want to blow out the stack
- // if a thread accumulates a stupendous amount of garbage
- AutoreleasePoolPage *page = this;
- while (page->child) page = page->child;
- AutoreleasePoolPage *deathptr;
- do {
- deathptr = page;
- page = page->parent;
- if (page) {
- page->unprotect();
- page->child = nil;
- page->protect();
- }
- delete deathptr;
- } while (deathptr != this);
- }
- static void tls_dealloc(void *p)
- {
- if (p == (void*)EMPTY_POOL_PLACEHOLDER) {
- // No objects or pool pages to clean up here.
- return;
- }
- // reinstate TLS value while we work
- setHotPage((AutoreleasePoolPage *)p);
- if (AutoreleasePoolPage *page = coldPage()) {
- if (!page->empty()) objc_autoreleasePoolPop(page->begin()); // pop all of the pools
- if (slowpath(DebugMissingPools || DebugPoolAllocation)) {
- // pop() killed the pages already
- } else {
- page->kill(); // free all of the pages
- }
- }
-
- // clear TLS value so TLS destruction doesn't loop
- setHotPage(nil);
- }
- static AutoreleasePoolPage *pageForPointer(const void *p)
- {
- return pageForPointer((uintptr_t)p);
- }
- static AutoreleasePoolPage *pageForPointer(uintptr_t p)
- {
- AutoreleasePoolPage *result;
- uintptr_t offset = p % SIZE;
- ASSERT(offset >= sizeof(AutoreleasePoolPage));
- result = (AutoreleasePoolPage *)(p - offset);
- result->fastcheck();
- return result;
- }
- static inline bool haveEmptyPoolPlaceholder()
- {
- id *tls = (id *)tls_get_direct(key);
- return (tls == EMPTY_POOL_PLACEHOLDER);
- }
- static inline id* setEmptyPoolPlaceholder()
- {
- ASSERT(tls_get_direct(key) == nil);
- tls_set_direct(key, (void *)EMPTY_POOL_PLACEHOLDER);
- return EMPTY_POOL_PLACEHOLDER;
- }
- static inline AutoreleasePoolPage *hotPage()
- {
- AutoreleasePoolPage *result = (AutoreleasePoolPage *)
- tls_get_direct(key);
- if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil;
- if (result) result->fastcheck();
- return result;
- }
- static inline void setHotPage(AutoreleasePoolPage *page)
- {
- if (page) page->fastcheck();
- tls_set_direct(key, (void *)page);
- }
- static inline AutoreleasePoolPage *coldPage()
- {
- AutoreleasePoolPage *result = hotPage();
- if (result) {
- while (result->parent) {
- result = result->parent;
- result->fastcheck();
- }
- }
- return result;
- }
- static inline id *autoreleaseFast(id obj)
- {
- AutoreleasePoolPage *page = hotPage();
- if (page && !page->full()) {
- return page->add(obj);
- } else if (page) {
- return autoreleaseFullPage(obj, page);
- } else {
- return autoreleaseNoPage(obj);
- }
- }
- static __attribute__((noinline))
- id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
- {
- // The hot page is full.
- // Step to the next non-full page, adding a new page if necessary.
- // Then add the object to that page.
- ASSERT(page == hotPage());
- ASSERT(page->full() || DebugPoolAllocation);
- do {
- if (page->child) page = page->child;
- else page = new AutoreleasePoolPage(page);
- } while (page->full());
- setHotPage(page);
- return page->add(obj);
- }
- static __attribute__((noinline))
- id *autoreleaseNoPage(id obj)
- {
- // "No page" could mean no pool has been pushed
- // or an empty placeholder pool has been pushed and has no contents yet
- ASSERT(!hotPage());
- bool pushExtraBoundary = false;
- if (haveEmptyPoolPlaceholder()) {
- // We are pushing a second pool over the empty placeholder pool
- // or pushing the first object into the empty placeholder pool.
- // Before doing that, push a pool boundary on behalf of the pool
- // that is currently represented by the empty placeholder.
- pushExtraBoundary = true;
- }
- else if (obj != POOL_BOUNDARY && DebugMissingPools) {
- // We are pushing an object with no pool in place,
- // and no-pool debugging was requested by environment.
- _objc_inform("MISSING POOLS: (%p) Object %p of class %s "
- "autoreleased with no pool in place - "
- "just leaking - break on "
- "objc_autoreleaseNoPool() to debug",
- objc_thread_self(), (void*)obj, object_getClassName(obj));
- objc_autoreleaseNoPool(obj);
- return nil;
- }
- else if (obj == POOL_BOUNDARY && !DebugPoolAllocation) {
- // We are pushing a pool with no pool in place,
- // and alloc-per-pool debugging was not requested.
- // Install and return the empty pool placeholder.
- return setEmptyPoolPlaceholder();
- }
- // We are pushing an object or a non-placeholder'd pool.
- // Install the first page.
- AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
- setHotPage(page);
-
- // Push a boundary on behalf of the previously-placeholder'd pool.
- if (pushExtraBoundary) {
- page->add(POOL_BOUNDARY);
- }
-
- // Push the requested object or pool.
- return page->add(obj);
- }
- static __attribute__((noinline))
- id *autoreleaseNewPage(id obj)
- {
- AutoreleasePoolPage *page = hotPage();
- if (page) return autoreleaseFullPage(obj, page);
- else return autoreleaseNoPage(obj);
- }
- public:
- static inline id autorelease(id obj)
- {
- ASSERT(obj);
- ASSERT(!obj->isTaggedPointer());
- id *dest __unused = autoreleaseFast(obj);
- ASSERT(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj);
- return obj;
- }
- static inline void *push()
- {
- id *dest;
- if (slowpath(DebugPoolAllocation)) {
- // Each autorelease pool starts on a new pool page.
- dest = autoreleaseNewPage(POOL_BOUNDARY);
- } else {
- dest = autoreleaseFast(POOL_BOUNDARY);
- }
- ASSERT(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
- return dest;
- }
- __attribute__((noinline, cold))
- static void badPop(void *token)
- {
- // Error. For bincompat purposes this is not
- // fatal in executables built with old SDKs.
- if (DebugPoolAllocation || sdkIsAtLeast(10_12, 10_0, 10_0, 3_0, 2_0)) {
- // OBJC_DEBUG_POOL_ALLOCATION or new SDK. Bad pop is fatal.
- _objc_fatal
- ("Invalid or prematurely-freed autorelease pool %p.", token);
- }
- // Old SDK. Bad pop is warned once.
- static bool complained = false;
- if (!complained) {
- complained = true;
- _objc_inform_now_and_on_crash
- ("Invalid or prematurely-freed autorelease pool %p. "
- "Set a breakpoint on objc_autoreleasePoolInvalid to debug. "
- "Proceeding anyway because the app is old "
- "(SDK version " SDK_FORMAT "). Memory errors are likely.",
- token, FORMAT_SDK(sdkVersion()));
- }
- objc_autoreleasePoolInvalid(token);
- }
- template<bool allowDebug>
- static void
- popPage(void *token, AutoreleasePoolPage *page, id *stop)
- {
- if (allowDebug && PrintPoolHiwat) printHiwat();
- page->releaseUntil(stop);
- // memory: delete empty children
- if (allowDebug && DebugPoolAllocation && page->empty()) {
- // special case: delete everything during page-per-pool debugging
- AutoreleasePoolPage *parent = page->parent;
- page->kill();
- setHotPage(parent);
- } else if (allowDebug && DebugMissingPools && page->empty() && !page->parent) {
- // special case: delete everything for pop(top)
- // when debugging missing autorelease pools
- page->kill();
- setHotPage(nil);
- } else if (page->child) {
- // hysteresis: keep one empty child if page is more than half full
- if (page->lessThanHalfFull()) {
- page->child->kill();
- }
- else if (page->child->child) {
- page->child->child->kill();
- }
- }
- }
- __attribute__((noinline, cold))
- static void
- popPageDebug(void *token, AutoreleasePoolPage *page, id *stop)
- {
- popPage<true>(token, page, stop);
- }
- static inline void
- pop(void *token)
- {
- AutoreleasePoolPage *page;
- id *stop;
- if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
- // Popping the top-level placeholder pool.
- page = hotPage();
- if (!page) {
- // Pool was never used. Clear the placeholder.
- return setHotPage(nil);
- }
- // Pool was used. Pop its contents normally.
- // Pool pages remain allocated for re-use as usual.
- page = coldPage();
- token = page->begin();
- } else {
- page = pageForPointer(token);
- }
- stop = (id *)token;
- if (*stop != POOL_BOUNDARY) {
- if (stop == page->begin() && !page->parent) {
- // Start of coldest page may correctly not be POOL_BOUNDARY:
- // 1. top-level pool is popped, leaving the cold page in place
- // 2. an object is autoreleased with no pool
- } else {
- // Error. For bincompat purposes this is not
- // fatal in executables built with old SDKs.
- return badPop(token);
- }
- }
- if (slowpath(PrintPoolHiwat || DebugPoolAllocation || DebugMissingPools)) {
- return popPageDebug(token, page, stop);
- }
- return popPage<false>(token, page, stop);
- }
- static void init()
- {
- int r __unused = pthread_key_init_np(AutoreleasePoolPage::key,
- AutoreleasePoolPage::tls_dealloc);
- ASSERT(r == 0);
- }
- __attribute__((noinline, cold))
- void print()
- {
- _objc_inform("[%p] ................ PAGE %s %s %s", this,
- full() ? "(full)" : "",
- this == hotPage() ? "(hot)" : "",
- this == coldPage() ? "(cold)" : "");
- check(false);
- for (id *p = begin(); p < next; p++) {
- if (*p == POOL_BOUNDARY) {
- _objc_inform("[%p] ################ POOL %p", p, p);
- } else {
- _objc_inform("[%p] %#16lx %s",
- p, (unsigned long)*p, object_getClassName(*p));
- }
- }
- }
- __attribute__((noinline, cold))
- static void printAll()
- {
- _objc_inform("##############");
- _objc_inform("AUTORELEASE POOLS for thread %p", objc_thread_self());
- AutoreleasePoolPage *page;
- ptrdiff_t objects = 0;
- for (page = coldPage(); page; page = page->child) {
- objects += page->next - page->begin();
- }
- _objc_inform("%llu releases pending.", (unsigned long long)objects);
- if (haveEmptyPoolPlaceholder()) {
- _objc_inform("[%p] ................ PAGE (placeholder)",
- EMPTY_POOL_PLACEHOLDER);
- _objc_inform("[%p] ################ POOL (placeholder)",
- EMPTY_POOL_PLACEHOLDER);
- }
- else {
- for (page = coldPage(); page; page = page->child) {
- page->print();
- }
- }
- _objc_inform("##############");
- }
- __attribute__((noinline, cold))
- static void printHiwat()
- {
- // Check and propagate high water mark
- // Ignore high water marks under 256 to suppress noise.
- AutoreleasePoolPage *p = hotPage();
- uint32_t mark = p->depth*COUNT + (uint32_t)(p->next - p->begin());
- if (mark > p->hiwat && mark > 256) {
- for( ; p; p = p->parent) {
- p->unprotect();
- p->hiwat = mark;
- p->protect();
- }
- _objc_inform("POOL HIGHWATER: new high water mark of %u "
- "pending releases for thread %p:",
- mark, objc_thread_self());
- void *stack[128];
- int count = backtrace(stack, sizeof(stack)/sizeof(stack[0]));
- char **sym = backtrace_symbols(stack, count);
- for (int i = 0; i < count; i++) {
- _objc_inform("POOL HIGHWATER: %s", sym[i]);
- }
- free(sym);
- }
- }
- #undef POOL_BOUNDARY
- };
- /***********************************************************************
- * Slow paths for inline control
- **********************************************************************/
- #if SUPPORT_NONPOINTER_ISA
- NEVER_INLINE id
- objc_object::rootRetain_overflow(bool tryRetain)
- {
- return rootRetain(tryRetain, true);
- }
- NEVER_INLINE uintptr_t
- objc_object::rootRelease_underflow(bool performDealloc)
- {
- return rootRelease(performDealloc, true);
- }
- // Slow path of clearDeallocating()
- // for objects with nonpointer isa
- // that were ever weakly referenced
- // or whose retain count ever overflowed to the side table.
- NEVER_INLINE void
- objc_object::clearDeallocating_slow()
- {
- ASSERT(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
- SideTable& table = SideTables()[this];
- table.lock();
- if (isa.weakly_referenced) {
- weak_clear_no_lock(&table.weak_table, (id)this);
- }
- if (isa.has_sidetable_rc) {
- table.refcnts.erase(this);
- }
- table.unlock();
- }
- #endif
- __attribute__((noinline,used))
- id
- objc_object::rootAutorelease2()
- {
- ASSERT(!isTaggedPointer());
- return AutoreleasePoolPage::autorelease((id)this);
- }
- BREAKPOINT_FUNCTION(
- void objc_overrelease_during_dealloc_error(void)
- );
- NEVER_INLINE uintptr_t
- objc_object::overrelease_error()
- {
- _objc_inform_now_and_on_crash("%s object %p overreleased while already deallocating; break on objc_overrelease_during_dealloc_error to debug", object_getClassName((id)this), this);
- objc_overrelease_during_dealloc_error();
- return 0; // allow rootRelease() to tail-call this
- }
- /***********************************************************************
- * Retain count operations for side table.
- **********************************************************************/
- #if DEBUG
- // Used to assert that an object is not present in the side table.
- bool
- objc_object::sidetable_present()
- {
- bool result = false;
- SideTable& table = SideTables()[this];
- table.lock();
- RefcountMap::iterator it = table.refcnts.find(this);
- if (it != table.refcnts.end()) result = true;
- if (weak_is_registered_no_lock(&table.weak_table, (id)this)) result = true;
- table.unlock();
- return result;
- }
- #endif
- #if SUPPORT_NONPOINTER_ISA
- void
- objc_object::sidetable_lock()
- {
- SideTable& table = SideTables()[this];
- table.lock();
- }
- void
- objc_object::sidetable_unlock()
- {
- SideTable& table = SideTables()[this];
- table.unlock();
- }
- // Move the entire retain count to the side table,
- // as well as isDeallocating and weaklyReferenced.
- void
- objc_object::sidetable_moveExtraRC_nolock(size_t extra_rc,
- bool isDeallocating,
- bool weaklyReferenced)
- {
- ASSERT(!isa.nonpointer); // should already be changed to raw pointer
- SideTable& table = SideTables()[this];
- size_t& refcntStorage = table.refcnts[this];
- size_t oldRefcnt = refcntStorage;
- // not deallocating - that was in the isa
- ASSERT((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);
- ASSERT((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);
- uintptr_t carry;
- size_t refcnt = addc(oldRefcnt, extra_rc << SIDE_TABLE_RC_SHIFT, 0, &carry);
- if (carry) refcnt = SIDE_TABLE_RC_PINNED;
- if (isDeallocating) refcnt |= SIDE_TABLE_DEALLOCATING;
- if (weaklyReferenced) refcnt |= SIDE_TABLE_WEAKLY_REFERENCED;
- refcntStorage = refcnt;
- }
- // Move some retain counts to the side table from the isa field.
- // Returns true if the object is now pinned.
- bool
- objc_object::sidetable_addExtraRC_nolock(size_t delta_rc)
- {
- ASSERT(isa.nonpointer);
- SideTable& table = SideTables()[this];
- size_t& refcntStorage = table.refcnts[this];
- size_t oldRefcnt = refcntStorage;
- // isa-side bits should not be set here
- ASSERT((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);
- ASSERT((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);
- if (oldRefcnt & SIDE_TABLE_RC_PINNED) return true;
- uintptr_t carry;
- size_t newRefcnt =
- addc(oldRefcnt, delta_rc << SIDE_TABLE_RC_SHIFT, 0, &carry);
- if (carry) {
- refcntStorage =
- SIDE_TABLE_RC_PINNED | (oldRefcnt & SIDE_TABLE_FLAG_MASK);
- return true;
- }
- else {
- refcntStorage = newRefcnt;
- return false;
- }
- }
- // Move some retain counts from the side table to the isa field.
- // Returns the actual count subtracted, which may be less than the request.
- size_t
- objc_object::sidetable_subExtraRC_nolock(size_t delta_rc)
- {
- ASSERT(isa.nonpointer);
- SideTable& table = SideTables()[this];
- RefcountMap::iterator it = table.refcnts.find(this);
- if (it == table.refcnts.end() || it->second == 0) {
- // Side table retain count is zero. Can't borrow.
- return 0;
- }
- size_t oldRefcnt = it->second;
- // isa-side bits should not be set here
- ASSERT((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);
- ASSERT((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);
- size_t newRefcnt = oldRefcnt - (delta_rc << SIDE_TABLE_RC_SHIFT);
- ASSERT(oldRefcnt > newRefcnt); // shouldn't underflow
- it->second = newRefcnt;
- return delta_rc;
- }
- size_t
- objc_object::sidetable_getExtraRC_nolock()
- {
- ASSERT(isa.nonpointer);
- SideTable& table = SideTables()[this];
- RefcountMap::iterator it = table.refcnts.find(this);
- if (it == table.refcnts.end()) return 0;
- else return it->second >> SIDE_TABLE_RC_SHIFT;
- }
- // SUPPORT_NONPOINTER_ISA
- #endif
- id
- objc_object::sidetable_retain()
- {
- #if SUPPORT_NONPOINTER_ISA
- ASSERT(!isa.nonpointer);
- #endif
- SideTable& table = SideTables()[this];
-
- table.lock();
- size_t& refcntStorage = table.refcnts[this];
- if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
- refcntStorage += SIDE_TABLE_RC_ONE;
- }
- table.unlock();
- return (id)this;
- }
- bool
- objc_object::sidetable_tryRetain()
- {
- #if SUPPORT_NONPOINTER_ISA
- ASSERT(!isa.nonpointer);
- #endif
- SideTable& table = SideTables()[this];
- // NO SPINLOCK HERE
- // _objc_rootTryRetain() is called exclusively by _objc_loadWeak(),
- // which already acquired the lock on our behalf.
- // fixme can't do this efficiently with os_lock_handoff_s
- // if (table.slock == 0) {
- // _objc_fatal("Do not call -_tryRetain.");
- // }
- bool result = true;
- auto it = table.refcnts.try_emplace(this, SIDE_TABLE_RC_ONE);
- auto &refcnt = it.first->second;
- if (it.second) {
- // there was no entry
- } else if (refcnt & SIDE_TABLE_DEALLOCATING) {
- result = false;
- } else if (! (refcnt & SIDE_TABLE_RC_PINNED)) {
- refcnt += SIDE_TABLE_RC_ONE;
- }
-
- return result;
- }
- uintptr_t
- objc_object::sidetable_retainCount()
- {
- SideTable& table = SideTables()[this];
- size_t refcnt_result = 1;
-
- table.lock();
- RefcountMap::iterator it = table.refcnts.find(this);
- if (it != table.refcnts.end()) {
- // this is valid for SIDE_TABLE_RC_PINNED too
- refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT;
- }
- table.unlock();
- return refcnt_result;
- }
- bool
- objc_object::sidetable_isDeallocating()
- {
- SideTable& table = SideTables()[this];
- // NO SPINLOCK HERE
- // _objc_rootIsDeallocating() is called exclusively by _objc_storeWeak(),
- // which already acquired the lock on our behalf.
- // fixme can't do this efficiently with os_lock_handoff_s
- // if (table.slock == 0) {
- // _objc_fatal("Do not call -_isDeallocating.");
- // }
- RefcountMap::iterator it = table.refcnts.find(this);
- return (it != table.refcnts.end()) && (it->second & SIDE_TABLE_DEALLOCATING);
- }
- bool
- objc_object::sidetable_isWeaklyReferenced()
- {
- bool result = false;
- SideTable& table = SideTables()[this];
- table.lock();
- RefcountMap::iterator it = table.refcnts.find(this);
- if (it != table.refcnts.end()) {
- result = it->second & SIDE_TABLE_WEAKLY_REFERENCED;
- }
- table.unlock();
- return result;
- }
- void
- objc_object::sidetable_setWeaklyReferenced_nolock()
- {
- #if SUPPORT_NONPOINTER_ISA
- ASSERT(!isa.nonpointer);
- #endif
- SideTable& table = SideTables()[this];
- table.refcnts[this] |= SIDE_TABLE_WEAKLY_REFERENCED;
- }
- // rdar://20206767
- // return uintptr_t instead of bool so that the various raw-isa
- // -release paths all return zero in eax
- uintptr_t
- objc_object::sidetable_release(bool performDealloc)
- {
- #if SUPPORT_NONPOINTER_ISA
- ASSERT(!isa.nonpointer);
- #endif
- SideTable& table = SideTables()[this];
- bool do_dealloc = false;
- table.lock();
- auto it = table.refcnts.try_emplace(this, SIDE_TABLE_DEALLOCATING);
- auto &refcnt = it.first->second;
- if (it.second) {
- do_dealloc = true;
- } else if (refcnt < SIDE_TABLE_DEALLOCATING) {
- // SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
- do_dealloc = true;
- refcnt |= SIDE_TABLE_DEALLOCATING;
- } else if (! (refcnt & SIDE_TABLE_RC_PINNED)) {
- refcnt -= SIDE_TABLE_RC_ONE;
- }
- table.unlock();
- if (do_dealloc && performDealloc) {
- ((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
- }
- return do_dealloc;
- }
- void
- objc_object::sidetable_clearDeallocating()
- {
- SideTable& table = SideTables()[this];
- // clear any weak table items
- // clear extra retain count and deallocating bit
- // (fixme warn or abort if extra retain count == 0 ?)
- table.lock();
- RefcountMap::iterator it = table.refcnts.find(this);
- if (it != table.refcnts.end()) {
- if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
- weak_clear_no_lock(&table.weak_table, (id)this);
- }
- table.refcnts.erase(it);
- }
- table.unlock();
- }
- /***********************************************************************
- * Optimized retain/release/autorelease entrypoints
- **********************************************************************/
- #if __OBJC2__
- __attribute__((aligned(16), flatten, noinline))
- id
- objc_retain(id obj)
- {
- if (!obj) return obj;
- if (obj->isTaggedPointer()) return obj;
- return obj->retain();
- }
- __attribute__((aligned(16), flatten, noinline))
- void
- objc_release(id obj)
- {
- if (!obj) return;
- if (obj->isTaggedPointer()) return;
- return obj->release();
- }
- __attribute__((aligned(16), flatten, noinline))
- id
- objc_autorelease(id obj)
- {
- if (!obj) return obj;
- if (obj->isTaggedPointer()) return obj;
- return obj->autorelease();
- }
- // OBJC2
- #else
- // not OBJC2
- id objc_retain(id obj) { return [obj retain]; }
- void objc_release(id obj) { [obj release]; }
- id objc_autorelease(id obj) { return [obj autorelease]; }
- #endif
- /***********************************************************************
- * Basic operations for root class implementations a.k.a. _objc_root*()
- **********************************************************************/
- bool
- _objc_rootTryRetain(id obj)
- {
- ASSERT(obj);
- return obj->rootTryRetain();
- }
- bool
- _objc_rootIsDeallocating(id obj)
- {
- ASSERT(obj);
- return obj->rootIsDeallocating();
- }
- void
- objc_clear_deallocating(id obj)
- {
- ASSERT(obj);
- if (obj->isTaggedPointer()) return;
- obj->clearDeallocating();
- }
- bool
- _objc_rootReleaseWasZero(id obj)
- {
- ASSERT(obj);
- return obj->rootReleaseShouldDealloc();
- }
- NEVER_INLINE id
- _objc_rootAutorelease(id obj)
- {
- ASSERT(obj);
- return obj->rootAutorelease();
- }
- uintptr_t
- _objc_rootRetainCount(id obj)
- {
- ASSERT(obj);
- return obj->rootRetainCount();
- }
- NEVER_INLINE id
- _objc_rootRetain(id obj)
- {
- ASSERT(obj);
- return obj->rootRetain();
- }
- NEVER_INLINE void
- _objc_rootRelease(id obj)
- {
- ASSERT(obj);
- obj->rootRelease();
- }
- // Call [cls alloc] or [cls allocWithZone:nil], with appropriate
- // shortcutting optimizations.
- static ALWAYS_INLINE id
- callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
- {
- #if __OBJC2__
- if (slowpath(checkNil && !cls)) return nil;
- if (fastpath(!cls->ISA()->hasCustomAWZ())) {
- return _objc_rootAllocWithZone(cls, nil);
- }
- #endif
- // No shortcuts available.
- if (allocWithZone) {
- return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
- }
- return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
- }
- // Base class implementation of +alloc. cls is not nil.
- // Calls [cls allocWithZone:nil].
- id
- _objc_rootAlloc(Class cls)
- {
- return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
- }
- // Calls [cls alloc].
- id
- objc_alloc(Class cls)
- {
- return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
- }
- // Calls [cls allocWithZone:nil].
- id
- objc_allocWithZone(Class cls)
- {
- return callAlloc(cls, true/*checkNil*/, true/*allocWithZone*/);
- }
- // Calls [[cls alloc] init].
- id
- objc_alloc_init(Class cls)
- {
- return [callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/) init];
- }
- // Calls [cls new]
- id
- objc_opt_new(Class cls)
- {
- #if __OBJC2__
- if (fastpath(cls && !cls->ISA()->hasCustomCore())) {
- return [callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/) init];
- }
- #endif
- return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(new));
- }
- // Calls [obj self]
- id
- objc_opt_self(id obj)
- {
- #if __OBJC2__
- if (fastpath(!obj || obj->isTaggedPointer() || !obj->ISA()->hasCustomCore())) {
- return obj;
- }
- #endif
- return ((id(*)(id, SEL))objc_msgSend)(obj, @selector(self));
- }
- // Calls [obj class]
- Class
- objc_opt_class(id obj)
- {
- #if __OBJC2__
- if (slowpath(!obj)) return nil;
- Class cls = obj->getIsa();
- if (fastpath(!cls->hasCustomCore())) {
- return cls->isMetaClass() ? obj : cls;
- }
- #endif
- return ((Class(*)(id, SEL))objc_msgSend)(obj, @selector(class));
- }
- // Calls [obj isKindOfClass]
- BOOL
- objc_opt_isKindOfClass(id obj, Class otherClass)
- {
- #if __OBJC2__
- if (slowpath(!obj)) return NO;
- Class cls = obj->getIsa();
- if (fastpath(!cls->hasCustomCore())) {
- for (Class tcls = cls; tcls; tcls = tcls->superclass) {
- if (tcls == otherClass) return YES;
- }
- return NO;
- }
- #endif
- return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
- }
- // Calls [obj respondsToSelector]
- BOOL
- objc_opt_respondsToSelector(id obj, SEL sel)
- {
- #if __OBJC2__
- if (slowpath(!obj)) return NO;
- Class cls = obj->getIsa();
- if (fastpath(!cls->hasCustomCore())) {
- return class_respondsToSelector_inst(obj, sel, cls);
- }
- #endif
- return ((BOOL(*)(id, SEL, SEL))objc_msgSend)(obj, @selector(respondsToSelector:), sel);
- }
- void
- _objc_rootDealloc(id obj)
- {
- ASSERT(obj);
- obj->rootDealloc();
- }
- void
- _objc_rootFinalize(id obj __unused)
- {
- ASSERT(obj);
- _objc_fatal("_objc_rootFinalize called with garbage collection off");
- }
- id
- _objc_rootInit(id obj)
- {
- // In practice, it will be hard to rely on this function.
- // Many classes do not properly chain -init calls.
- return obj;
- }
- malloc_zone_t *
- _objc_rootZone(id obj)
- {
- (void)obj;
- #if __OBJC2__
- // allocWithZone under __OBJC2__ ignores the zone parameter
- return malloc_default_zone();
- #else
- malloc_zone_t *rval = malloc_zone_from_ptr(obj);
- return rval ? rval : malloc_default_zone();
- #endif
- }
- uintptr_t
- _objc_rootHash(id obj)
- {
- return (uintptr_t)obj;
- }
- void *
- objc_autoreleasePoolPush(void)
- {
- return AutoreleasePoolPage::push();
- }
- NEVER_INLINE
- void
- objc_autoreleasePoolPop(void *ctxt)
- {
- AutoreleasePoolPage::pop(ctxt);
- }
- void *
- _objc_autoreleasePoolPush(void)
- {
- return objc_autoreleasePoolPush();
- }
- void
- _objc_autoreleasePoolPop(void *ctxt)
- {
- objc_autoreleasePoolPop(ctxt);
- }
- void
- _objc_autoreleasePoolPrint(void)
- {
- AutoreleasePoolPage::printAll();
- }
- // Same as objc_release but suitable for tail-calling
- // if you need the value back and don't want to push a frame before this point.
- __attribute__((noinline))
- static id
- objc_releaseAndReturn(id obj)
- {
- objc_release(obj);
- return obj;
- }
- // Same as objc_retainAutorelease but suitable for tail-calling
- // if you don't want to push a frame before this point.
- __attribute__((noinline))
- static id
- objc_retainAutoreleaseAndReturn(id obj)
- {
- return objc_retainAutorelease(obj);
- }
- // Prepare a value at +1 for return through a +0 autoreleasing convention.
- id
- objc_autoreleaseReturnValue(id obj)
- {
- if (prepareOptimizedReturn(ReturnAtPlus1)) return obj;
- return objc_autorelease(obj);
- }
- // Prepare a value at +0 for return through a +0 autoreleasing convention.
- id
- objc_retainAutoreleaseReturnValue(id obj)
- {
- if (prepareOptimizedReturn(ReturnAtPlus0)) return obj;
- // not objc_autoreleaseReturnValue(objc_retain(obj))
- // because we don't need another optimization attempt
- return objc_retainAutoreleaseAndReturn(obj);
- }
- // Accept a value returned through a +0 autoreleasing convention for use at +1.
- id
- objc_retainAutoreleasedReturnValue(id obj)
- {
- if (acceptOptimizedReturn() == ReturnAtPlus1) return obj;
- return objc_retain(obj);
- }
- // Accept a value returned through a +0 autoreleasing convention for use at +0.
- id
- objc_unsafeClaimAutoreleasedReturnValue(id obj)
- {
- if (acceptOptimizedReturn() == ReturnAtPlus0) return obj;
- return objc_releaseAndReturn(obj);
- }
- id
- objc_retainAutorelease(id obj)
- {
- return objc_autorelease(objc_retain(obj));
- }
- void
- _objc_deallocOnMainThreadHelper(void *context)
- {
- id obj = (id)context;
- [obj dealloc];
- }
- // convert objc_objectptr_t to id, callee must take ownership.
- id objc_retainedObject(objc_objectptr_t pointer) { return (id)pointer; }
- // convert objc_objectptr_t to id, without ownership transfer.
- id objc_unretainedObject(objc_objectptr_t pointer) { return (id)pointer; }
- // convert id to objc_objectptr_t, no ownership transfer.
- objc_objectptr_t objc_unretainedPointer(id object) { return object; }
- void arr_init(void)
- {
- AutoreleasePoolPage::init();
- SideTablesMap.init();
- _objc_associations_init();
- }
- #if SUPPORT_TAGGED_POINTERS
- // Placeholder for old debuggers. When they inspect an
- // extended tagged pointer object they will see this isa.
- @interface __NSUnrecognizedTaggedPointer : NSObject
- @end
- __attribute__((objc_nonlazy_class))
- @implementation __NSUnrecognizedTaggedPointer
- -(id) retain { return self; }
- -(oneway void) release { }
- -(id) autorelease { return self; }
- @end
- #endif
- __attribute__((objc_nonlazy_class))
- @implementation NSObject
- + (void)initialize {
- }
- + (id)self {
- return (id)self;
- }
- - (id)self {
- return self;
- }
- + (Class)class {
- return self;
- }
- - (Class)class {
- return object_getClass(self);
- }
- + (Class)superclass {
- return self->superclass;
- }
- - (Class)superclass {
- return [self class]->superclass;
- }
- + (BOOL)isMemberOfClass:(Class)cls {
- return self->ISA() == cls;
- }
- - (BOOL)isMemberOfClass:(Class)cls {
- return [self class] == cls;
- }
- + (BOOL)isKindOfClass:(Class)cls {
- for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
- if (tcls == cls) return YES;
- }
- return NO;
- }
- - (BOOL)isKindOfClass:(Class)cls {
- for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
- if (tcls == cls) return YES;
- }
- return NO;
- }
- + (BOOL)isSubclassOfClass:(Class)cls {
- for (Class tcls = self; tcls; tcls = tcls->superclass) {
- if (tcls == cls) return YES;
- }
- return NO;
- }
- + (BOOL)isAncestorOfObject:(NSObject *)obj {
- for (Class tcls = [obj class]; tcls; tcls = tcls->superclass) {
- if (tcls == self) return YES;
- }
- return NO;
- }
- + (BOOL)instancesRespondToSelector:(SEL)sel {
- return class_respondsToSelector_inst(nil, sel, self);
- }
- + (BOOL)respondsToSelector:(SEL)sel {
- return class_respondsToSelector_inst(self, sel, self->ISA());
- }
- - (BOOL)respondsToSelector:(SEL)sel {
- return class_respondsToSelector_inst(self, sel, [self class]);
- }
- + (BOOL)conformsToProtocol:(Protocol *)protocol {
- if (!protocol) return NO;
- for (Class tcls = self; tcls; tcls = tcls->superclass) {
- if (class_conformsToProtocol(tcls, protocol)) return YES;
- }
- return NO;
- }
- - (BOOL)conformsToProtocol:(Protocol *)protocol {
- if (!protocol) return NO;
- for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
- if (class_conformsToProtocol(tcls, protocol)) return YES;
- }
- return NO;
- }
- + (NSUInteger)hash {
- return _objc_rootHash(self);
- }
- - (NSUInteger)hash {
- return _objc_rootHash(self);
- }
- + (BOOL)isEqual:(id)obj {
- return obj == (id)self;
- }
- - (BOOL)isEqual:(id)obj {
- return obj == self;
- }
- + (BOOL)isFault {
- return NO;
- }
- - (BOOL)isFault {
- return NO;
- }
- + (BOOL)isProxy {
- return NO;
- }
- - (BOOL)isProxy {
- return NO;
- }
- + (IMP)instanceMethodForSelector:(SEL)sel {
- if (!sel) [self doesNotRecognizeSelector:sel];
- return class_getMethodImplementation(self, sel);
- }
- + (IMP)methodForSelector:(SEL)sel {
- if (!sel) [self doesNotRecognizeSelector:sel];
- return object_getMethodImplementation((id)self, sel);
- }
- - (IMP)methodForSelector:(SEL)sel {
- if (!sel) [self doesNotRecognizeSelector:sel];
- return object_getMethodImplementation(self, sel);
- }
- + (BOOL)resolveClassMethod:(SEL)sel {
- return NO;
- }
- + (BOOL)resolveInstanceMethod:(SEL)sel {
- return NO;
- }
- // Replaced by CF (throws an NSException)
- + (void)doesNotRecognizeSelector:(SEL)sel {
- _objc_fatal("+[%s %s]: unrecognized selector sent to instance %p",
- class_getName(self), sel_getName(sel), self);
- }
- // Replaced by CF (throws an NSException)
- - (void)doesNotRecognizeSelector:(SEL)sel {
- _objc_fatal("-[%s %s]: unrecognized selector sent to instance %p",
- object_getClassName(self), sel_getName(sel), self);
- }
- + (id)performSelector:(SEL)sel {
- if (!sel) [self doesNotRecognizeSelector:sel];
- return ((id(*)(id, SEL))objc_msgSend)((id)self, sel);
- }
- + (id)performSelector:(SEL)sel withObject:(id)obj {
- if (!sel) [self doesNotRecognizeSelector:sel];
- return ((id(*)(id, SEL, id))objc_msgSend)((id)self, sel, obj);
- }
- + (id)performSelector:(SEL)sel withObject:(id)obj1 withObject:(id)obj2 {
- if (!sel) [self doesNotRecognizeSelector:sel];
- return ((id(*)(id, SEL, id, id))objc_msgSend)((id)self, sel, obj1, obj2);
- }
- - (id)performSelector:(SEL)sel {
- if (!sel) [self doesNotRecognizeSelector:sel];
- return ((id(*)(id, SEL))objc_msgSend)(self, sel);
- }
- - (id)performSelector:(SEL)sel withObject:(id)obj {
- if (!sel) [self doesNotRecognizeSelector:sel];
- return ((id(*)(id, SEL, id))objc_msgSend)(self, sel, obj);
- }
- - (id)performSelector:(SEL)sel withObject:(id)obj1 withObject:(id)obj2 {
- if (!sel) [self doesNotRecognizeSelector:sel];
- return ((id(*)(id, SEL, id, id))objc_msgSend)(self, sel, obj1, obj2);
- }
- // Replaced by CF (returns an NSMethodSignature)
- + (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)sel {
- _objc_fatal("+[NSObject instanceMethodSignatureForSelector:] "
- "not available without CoreFoundation");
- }
- // Replaced by CF (returns an NSMethodSignature)
- + (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
- _objc_fatal("+[NSObject methodSignatureForSelector:] "
- "not available without CoreFoundation");
- }
- // Replaced by CF (returns an NSMethodSignature)
- - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
- _objc_fatal("-[NSObject methodSignatureForSelector:] "
- "not available without CoreFoundation");
- }
- + (void)forwardInvocation:(NSInvocation *)invocation {
- [self doesNotRecognizeSelector:(invocation ? [invocation selector] : 0)];
- }
- - (void)forwardInvocation:(NSInvocation *)invocation {
- [self doesNotRecognizeSelector:(invocation ? [invocation selector] : 0)];
- }
- + (id)forwardingTargetForSelector:(SEL)sel {
- return nil;
- }
- - (id)forwardingTargetForSelector:(SEL)sel {
- return nil;
- }
- // Replaced by CF (returns an NSString)
- + (NSString *)description {
- return nil;
- }
- // Replaced by CF (returns an NSString)
- - (NSString *)description {
- return nil;
- }
- + (NSString *)debugDescription {
- return [self description];
- }
- - (NSString *)debugDescription {
- return [self description];
- }
- + (id)new {
- return [callAlloc(self, false/*checkNil*/) init];
- }
- + (id)retain {
- return (id)self;
- }
- // Replaced by ObjectAlloc
- - (id)retain {
- return _objc_rootRetain(self);
- }
- + (BOOL)_tryRetain {
- return YES;
- }
- // Replaced by ObjectAlloc
- - (BOOL)_tryRetain {
- return _objc_rootTryRetain(self);
- }
- + (BOOL)_isDeallocating {
- return NO;
- }
- - (BOOL)_isDeallocating {
- return _objc_rootIsDeallocating(self);
- }
- + (BOOL)allowsWeakReference {
- return YES;
- }
- + (BOOL)retainWeakReference {
- return YES;
- }
- - (BOOL)allowsWeakReference {
- return ! [self _isDeallocating];
- }
- - (BOOL)retainWeakReference {
- return [self _tryRetain];
- }
- + (oneway void)release {
- }
- // Replaced by ObjectAlloc
- - (oneway void)release {
- _objc_rootRelease(self);
- }
- + (id)autorelease {
- return (id)self;
- }
- // Replaced by ObjectAlloc
- - (id)autorelease {
- return _objc_rootAutorelease(self);
- }
- + (NSUInteger)retainCount {
- return ULONG_MAX;
- }
- - (NSUInteger)retainCount {
- return _objc_rootRetainCount(self);
- }
- + (id)alloc {
- return _objc_rootAlloc(self);
- }
- // Replaced by ObjectAlloc
- + (id)allocWithZone:(struct _NSZone *)zone {
- return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
- }
- // Replaced by CF (throws an NSException)
- + (id)init {
- return (id)self;
- }
- - (id)init {
- return _objc_rootInit(self);
- }
- // Replaced by CF (throws an NSException)
- + (void)dealloc {
- }
- // Replaced by NSZombies
- - (void)dealloc {
- _objc_rootDealloc(self);
- }
- // Previously used by GC. Now a placeholder for binary compatibility.
- - (void) finalize {
- }
- + (struct _NSZone *)zone {
- return (struct _NSZone *)_objc_rootZone(self);
- }
- - (struct _NSZone *)zone {
- return (struct _NSZone *)_objc_rootZone(self);
- }
- + (id)copy {
- return (id)self;
- }
- + (id)copyWithZone:(struct _NSZone *)zone {
- return (id)self;
- }
- - (id)copy {
- return [(id)self copyWithZone:nil];
- }
- + (id)mutableCopy {
- return (id)self;
- }
- + (id)mutableCopyWithZone:(struct _NSZone *)zone {
- return (id)self;
- }
- - (id)mutableCopy {
- return [(id)self mutableCopyWithZone:nil];
- }
- @end
|