Something went wrong. Try again.
Cider Isn't Darwin Emulation, Really
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629// Modified by Lubos Dolezel for Darling/* * Copyright (c) 2000-2008 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@ *//* * Copyright 1996 1995 by Open Software Foundation, Inc. 1997 1996 1995 1994 1993 1992 1991 * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appears in all copies and * that both the copyright notice and this permission notice appear in * supporting documentation. * * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE. * * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * *//* * MkLinux */
/* * POSIX Pthread Library */
#include "pthread_internals.h"#include "pthread_workqueue.h"
#include <assert.h>#include <stdio.h> /* For printf(). */#include <stdlib.h>#include <errno.h> /* For __mach_errno_addr() prototype. */#include <signal.h>#include <sys/time.h>#include <sys/resource.h>#include <sys/sysctl.h>#include <sys/queue.h>#include <sys/mman.h>#include <machine/vmparam.h>#include <mach/vm_statistics.h>#include <mach/mach_init.h>#define __APPLE_API_PRIVATE#include <machine/cpu_capabilities.h>#include <libkern/OSAtomic.h>#if defined(__ppc__)#include <libkern/OSCrossEndian.h>#endif#include <dispatch/private.h> /* for at_fork handlers */
extern int _pthread_setcancelstate_internal(int state, int *oldstate, int conforming);extern int __pthread_sigmask(int, const sigset_t *, sigset_t *);
#ifndef BUILDING_VARIANT /* [ */
__private_extern__ struct __pthread_list __pthread_head = TAILQ_HEAD_INITIALIZER(__pthread_head);
int32_t workq_targetconc[WORKQ_NUM_PRIOQUEUE];
/* Per-thread kernel support */extern void _pthread_set_self(pthread_t);extern void mig_init(int);static int _pthread_create_pthread_onstack(pthread_attr_t *attrs, void **stack, pthread_t *thread);static kern_return_t _pthread_free_pthread_onstack(pthread_t t, int freestruct, int termthread);static void _pthread_struct_init(pthread_t t, const pthread_attr_t *attrs, void * stack, size_t stacksize, int kernalloc, int nozero);static int _new_pthread_create_suspended(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg, int create_susp);
/* the registered libdispatch worker function */static void (*__libdispatch_workerfunction)(int, int, void *) = NULL;
/* Get CPU capabilities from the kernel */__private_extern__ void _init_cpu_capabilities(void);
/* Needed to tell the malloc subsystem we're going multithreaded */extern void set_malloc_singlethreaded(int);
/* Used when we need to call into the kernel with no reply port */extern pthread_lock_t reply_port_lock;int _pthread_find_thread(pthread_t thread);
/* Mach message used to notify that a thread needs to be reaped */
typedef struct _pthread_reap_msg_t { mach_msg_header_t header; pthread_t thread; mach_msg_trailer_t trailer;} pthread_reap_msg_t;
/* Utilitie */
__private_extern__ uintptr_t commpage_pfz_base=0;
void __pthread_pfz_setup(const char *apple[]) __attribute__ ((visibility ("hidden")));
static uintptr_t __pfz_from_kernel(const char *str){ unsigned long tmpval; /* Skip over key to the first value */ str = strchr(str, '='); if (str == NULL) return 0; str++; tmpval = strtoul(str, NULL, 0); /* may err by 0 or ULONG_MAX */ if (tmpval == ULONG_MAX) tmpval = 0;
return (uintptr_t) tmpval;}
void__pthread_pfz_setup(const char *apple[]){ const char **p; for (p = apple; p && *p; p++) { /* checking if matching apple variable is at begining */ if (strstr(*p, "pfz=") == *p) { commpage_pfz_base = __pfz_from_kernel(*p); bzero(*p,strlen(*p)); break; } }
if (commpage_pfz_base == 0) commpage_pfz_base = _COMM_PAGE_TEXT_START;
return;}
/* We'll implement this when the main thread is a pthread *//* Use the local _pthread struct to avoid malloc before our MiG reply port is set */static struct _pthread _thread = {0};
/* This global should be used (carefully) by anyone needing to know if a ** pthread has been created.*/int __is_threaded = 0;/* _pthread_count is protected by _pthread_list_lock */static int _pthread_count = 1;int __unix_conforming = 0;static int __workqueue_newspis = 0;static int __workqueue_oldspis = 0;__private_extern__ size_t pthreadsize = 0;
/* under rosetta we will use old style creation of threads */static int __oldstyle = 0;
__private_extern__ pthread_lock_t _pthread_list_lock = LOCK_INITIALIZER;
/* Same implementation as LOCK, but without the __is_threaded check */int _spin_tries = 0;extern kern_return_t syscall_thread_switch(mach_port_name_t, int, mach_msg_timeout_t);__private_extern__ void _spin_lock_retry(pthread_lock_t *lock){ int tries = _spin_tries; do { if (tries-- > 0) continue; syscall_thread_switch(THREAD_NULL, SWITCH_OPTION_DEPRESS, 1); tries = _spin_tries; } while(!_spin_lock_try(lock));}
static mach_port_t thread_recycle_port = MACH_PORT_NULL;
/* These are used to keep track of a semaphore pool shared by mutexes and condition** variables.*/
static semaphore_t *sem_pool = NULL;static int sem_pool_count = 0;static int sem_pool_current = 0;static pthread_lock_t sem_pool_lock = LOCK_INITIALIZER;
static int default_priority;static int max_priority;static int min_priority;static int pthread_concurrency;
static OSSpinLock __workqueue_list_lock = OS_SPINLOCK_INIT;
static void _pthread_exit(pthread_t self, void *value_ptr) __dead2;static void _pthread_setcancelstate_exit(pthread_t self, void *value_ptr, int conforming);static pthread_attr_t _pthread_attr_default = {0};static void _pthread_workq_init(pthread_workqueue_t wq, const pthread_workqueue_attr_t * attr);static int kernel_workq_setup = 0;static volatile int32_t kernel_workq_count = 0;static volatile unsigned int user_workq_count = 0; /* number of outstanding workqueues */static volatile unsigned int user_workitem_count = 0; /* number of outstanding workitems */#define KERNEL_WORKQ_ELEM_MAX 64 /* Max number of elements in the kerrel */static int wqreadyprio = 0; /* current highest prio queue ready with items */
__private_extern__ struct __pthread_workitem_pool __pthread_workitem_pool_head = TAILQ_HEAD_INITIALIZER(__pthread_workitem_pool_head);__private_extern__ struct __pthread_workqueue_pool __pthread_workqueue_pool_head = TAILQ_HEAD_INITIALIZER(__pthread_workqueue_pool_head);
static struct _pthread_workitem * __workqueue_pool_ptr;static size_t __workqueue_pool_size = 0;static int __workqueue_nitems = 0;
struct _pthread_workqueue_head __pthread_workq0_head;struct _pthread_workqueue_head __pthread_workq1_head;struct _pthread_workqueue_head __pthread_workq2_head;struct _pthread_workqueue_head __pthread_workq3_head;pthread_workqueue_head_t __pthread_wq_head_tbl[WORKQ_NUM_PRIOQUEUE] = {&__pthread_workq0_head, &__pthread_workq1_head, &__pthread_workq2_head, &__pthread_workq3_head};
static void workqueue_list_lock(void);static void workqueue_list_unlock(void);static int valid_workq(pthread_workqueue_t);static void pick_nextworkqueue_droplock(void);static int post_nextworkitem(pthread_workqueue_t workq);static void _pthread_workq_return(pthread_t self);static pthread_workqueue_attr_t _pthread_wq_attr_default = {0};extern void start_wqthread(pthread_t self, mach_port_t kport, void * stackaddr, pthread_workitem_t item, int reuse);extern void thread_start(pthread_t self, mach_port_t kport, void *(*fun)(void *), void * funarg, size_t stacksize, unsigned int flags);static pthread_workitem_t alloc_workitem(void);static void free_workitem(pthread_workitem_t);static void grow_workitem(void);static pthread_workqueue_t alloc_workqueue(void);static void free_workqueue(pthread_workqueue_t);static int _pthread_work_internal_init(void);static void workqueue_exit(pthread_t self, pthread_workqueue_t workq, pthread_workitem_t item);void _pthread_fork_child_postinit();
void pthread_workqueue_atfork_prepare(void);void pthread_workqueue_atfork_parent(void);void pthread_workqueue_atfork_child(void);
extern void dispatch_atfork_prepare(void);extern void dispatch_atfork_parent(void);extern void dispatch_atfork_child(void);
/* workq_kernreturn commands */#define WQOPS_QUEUE_ADD 1#define WQOPS_QUEUE_REMOVE 2#define WQOPS_THREAD_RETURN 4#define WQOPS_THREAD_SETCONC 8#define WQOPS_QUEUE_NEWSPISUPP 0x10 /* this is to check for newer SPI support */#define WQOPS_QUEUE_REQTHREADS 0x20 /* request number of threads of a prio */
/* flag values for reuse field in the libc side _pthread_wqthread */#define WQ_FLAG_THREAD_PRIOMASK 0x0000ffff#define WQ_FLAG_THREAD_OVERCOMMIT 0x00010000 /* thread is with overcommit prio */#define WQ_FLAG_THREAD_REUSE 0x00020000 /* thread is being reused */#define WQ_FLAG_THREAD_NEWSPI 0x00040000 /* the call is with new SPIs */
#define WORKQUEUE_OVERCOMMIT 0x10000 /* the work_kernreturn() for overcommit in prio field */
/* * Flags filed passed to bsdthread_create and back in pthread_start 31 <---------------------------------> 0_________________________________________| flags(8) | policy(8) | importance(16) |-----------------------------------------*/__private_extern__void _pthread_start(pthread_t self, mach_port_t kport, void *(*fun)(void *), void * funarg, size_t stacksize, unsigned int flags);
__private_extern__void _pthread_wqthread(pthread_t self, mach_port_t kport, void * stackaddr, pthread_workitem_t item, int reuse);
#define PTHREAD_START_CUSTOM 0x01000000#define PTHREAD_START_SETSCHED 0x02000000#define PTHREAD_START_DETACHED 0x04000000#define PTHREAD_START_POLICY_BITSHIFT 16#define PTHREAD_START_POLICY_MASK 0xff#define PTHREAD_START_IMPORTANCE_MASK 0xffff
static int pthread_setschedparam_internal(pthread_t, mach_port_t, int, const struct sched_param *);extern pthread_t __bsdthread_create(void *(*func)(void *), void * func_arg, void * stack, pthread_t thread, unsigned int flags);extern int __bsdthread_register(void (*)(pthread_t, mach_port_t, void *(*)(void *), void *, size_t, unsigned int), void (*)(pthread_t, mach_port_t, void *, pthread_workitem_t, int), int,void (*)(pthread_t, mach_port_t, void *(*)(void *), void *, size_t, unsigned int), int32_t *,__uint64_t);extern int __bsdthread_terminate(void * freeaddr, size_t freesize, mach_port_t kport, mach_port_t joinsem);extern __uint64_t __thread_selfid( void );extern int __pthread_canceled(int);extern void _pthread_keys_init(void);extern int __pthread_kill(mach_port_t, int);extern int __pthread_markcancel(int);extern int __workq_open(void);
extern int __workq_kernreturn(int, pthread_workitem_t, int, int);
#if defined(__ppc__) || defined(__ppc64__)static const vm_address_t PTHREAD_STACK_HINT = 0xF0000000;#elif defined(__i386__) || defined(__x86_64__)static const vm_address_t PTHREAD_STACK_HINT = 0xB0000000;#elif defined(__arm__) static const vm_address_t PTHREAD_STACK_HINT = 0x30000000;#else#error Need to define a stack address hint for this architecture#endif
/* Set the base address to use as the stack pointer, before adjusting due to the ABI * The guardpages for stackoverflow protection is also allocated here * If the stack was already allocated(stackaddr in attr) then there are no guardpages * set up for the thread */
static int_pthread_allocate_stack(pthread_attr_t *attrs, void **stack){ kern_return_t kr; vm_address_t stackaddr; size_t guardsize;
assert(attrs->stacksize >= PTHREAD_STACK_MIN); if (attrs->stackaddr != NULL) { /* No guard pages setup in this case */ assert(((uintptr_t)attrs->stackaddr % vm_page_size) == 0); *stack = attrs->stackaddr; return 0; }
guardsize = attrs->guardsize; stackaddr = PTHREAD_STACK_HINT; kr = vm_map(mach_task_self(), &stackaddr, attrs->stacksize + guardsize, vm_page_size-1, VM_MAKE_TAG(VM_MEMORY_STACK)| VM_FLAGS_ANYWHERE , MEMORY_OBJECT_NULL, 0, FALSE, VM_PROT_DEFAULT, VM_PROT_ALL, VM_INHERIT_DEFAULT); if (kr != KERN_SUCCESS) kr = vm_allocate(mach_task_self(), &stackaddr, attrs->stacksize + guardsize, VM_MAKE_TAG(VM_MEMORY_STACK)| VM_FLAGS_ANYWHERE); if (kr != KERN_SUCCESS) { return EAGAIN; } /* The guard page is at the lowest address */ /* The stack base is the highest address */ if (guardsize) kr = vm_protect(mach_task_self(), stackaddr, guardsize, FALSE, VM_PROT_NONE); *stack = (void *)(stackaddr + attrs->stacksize + guardsize); return 0;}
static int_pthread_create_pthread_onstack(pthread_attr_t *attrs, void **stack, pthread_t *thread){ kern_return_t kr; pthread_t t; vm_address_t stackaddr; size_t guardsize, allocsize;
assert(attrs->stacksize >= PTHREAD_STACK_MIN);
if (attrs->stackaddr != NULL) { /* No guard pages setup in this case */ assert(((uintptr_t)attrs->stackaddr % vm_page_size) == 0); *stack = attrs->stackaddr; t = (pthread_t)malloc(pthreadsize); _pthread_struct_init(t, attrs, attrs->stackaddr, 0, 0, 0); t->freeStackOnExit = 0; t->freeaddr = 0; t->freesize = 0; *thread = t; return 0; }
guardsize = attrs->guardsize; allocsize = attrs->stacksize + guardsize + pthreadsize; stackaddr = PTHREAD_STACK_HINT; kr = vm_map(mach_task_self(), &stackaddr, allocsize, vm_page_size-1, VM_MAKE_TAG(VM_MEMORY_STACK)| VM_FLAGS_ANYWHERE , MEMORY_OBJECT_NULL, 0, FALSE, VM_PROT_DEFAULT, VM_PROT_ALL, VM_INHERIT_DEFAULT); if (kr != KERN_SUCCESS) kr = vm_allocate(mach_task_self(), &stackaddr, allocsize, VM_MAKE_TAG(VM_MEMORY_STACK)| VM_FLAGS_ANYWHERE); if (kr != KERN_SUCCESS) { return EAGAIN; } /* The guard page is at the lowest address */ /* The stack base is the highest address */ if (guardsize) kr = vm_protect(mach_task_self(), stackaddr, guardsize, FALSE, VM_PROT_NONE);
*stack = (void *)(stackaddr + attrs->stacksize + guardsize);
t = (pthread_t)(stackaddr + attrs->stacksize + guardsize); _pthread_struct_init(t, attrs, *stack, 0, 0, 1); t->kernalloc = 0; t->freesize = allocsize; t->freeaddr = (void *)stackaddr; t->freeStackOnExit = 1; *thread = t;
return 0;}
static kern_return_t_pthread_free_pthread_onstack(pthread_t t, int freestruct, int termthread){ kern_return_t res = 0; vm_address_t freeaddr; size_t freesize; int thread_count; mach_port_t kport; semaphore_t joinsem = SEMAPHORE_NULL;
#if PTH_TRACE __kdebug_trace(0x900001c, freestruct, termthread, 0, 0, 0);#endif kport = t->kernel_thread; joinsem = t->joiner_notify;
if (t->freeStackOnExit) { freeaddr = (vm_address_t)t->freeaddr; if (freestruct) freesize = t->stacksize + t->guardsize + pthreadsize; else freesize = t->stacksize + t->guardsize; if (termthread) { mig_dealloc_reply_port(MACH_PORT_NULL); LOCK(_pthread_list_lock); if (freestruct != 0) { TAILQ_REMOVE(&__pthread_head, t, plist); /* if parent has not returned from create yet keep pthread_t */#if PTH_LISTTRACE __kdebug_trace(0x9000010, t, 0, 0, 1, 0);#endif if (t->parentcheck == 0) freesize -= pthreadsize; } t->childexit = 1; thread_count = --_pthread_count; UNLOCK(_pthread_list_lock);
#if PTH_TRACE __kdebug_trace(0x9000020, freeaddr, freesize, kport, 1, 0);#endif if (thread_count <=0) exit(0); else __bsdthread_terminate((void *)freeaddr, freesize, kport, joinsem); LIBC_ABORT("thread %p didn't terminate", t); } else {#if PTH_TRACE __kdebug_trace(0x9000024, freeaddr, freesize, 0, 1, 0);#endif res = vm_deallocate(mach_task_self(), freeaddr, freesize); } } else { if (termthread) { mig_dealloc_reply_port(MACH_PORT_NULL); LOCK(_pthread_list_lock); if (freestruct != 0) { TAILQ_REMOVE(&__pthread_head, t, plist);#if PTH_LISTTRACE __kdebug_trace(0x9000010, t, 0, 0, 2, 0);#endif } thread_count = --_pthread_count; t->childexit = 1; UNLOCK(_pthread_list_lock);
if (freestruct) {#if PTH_TRACE __kdebug_trace(0x9000008, t, 0, 0, 2, 0);#endif free(t); }
freeaddr = 0; freesize = 0;#if PTH_TRACE __kdebug_trace(0x9000020, 0, 0, kport, 2, 0);#endif
if (thread_count <=0) exit(0); else __bsdthread_terminate(NULL, 0, kport, joinsem); LIBC_ABORT("thread %p didn't terminate", t); } else if (freestruct) { t->sig = _PTHREAD_NO_SIG;#if PTH_TRACE __kdebug_trace(0x9000024, t, 0, 0, 2, 0);#endif free(t); } } return(res);}
/* * Destroy a thread attribute structure */int pthread_attr_destroy(pthread_attr_t *attr){ if (attr->sig == _PTHREAD_ATTR_SIG) { attr->sig = 0; return (0); } else { return (EINVAL); /* Not an attribute structure! */ }}
/* * Get the 'detach' state from a thread attribute structure. * Note: written as a helper function for info hiding */int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate){ if (attr->sig == _PTHREAD_ATTR_SIG) { *detachstate = attr->detached; return (0); } else { return (EINVAL); /* Not an attribute structure! */ }}
/* * Get the 'inherit scheduling' info from a thread attribute structure. * Note: written as a helper function for info hiding */int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched){ if (attr->sig == _PTHREAD_ATTR_SIG) { *inheritsched = attr->inherit; return (0); } else { return (EINVAL); /* Not an attribute structure! */ }}
/* * Get the scheduling parameters from a thread attribute structure. * Note: written as a helper function for info hiding */int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param){ if (attr->sig == _PTHREAD_ATTR_SIG) { *param = attr->param; return (0); } else { return (EINVAL); /* Not an attribute structure! */ }}
/* * Get the scheduling policy from a thread attribute structure. * Note: written as a helper function for info hiding */int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy){ if (attr->sig == _PTHREAD_ATTR_SIG) { *policy = attr->policy; return (0); } else { return (EINVAL); /* Not an attribute structure! */ }}
/* Retain the existing stack size of 512K and not depend on Main thread default stack size */static const size_t DEFAULT_STACK_SIZE = (512*1024);/* * Initialize a thread attribute structure to default values. */int pthread_attr_init(pthread_attr_t *attr){ attr->stacksize = DEFAULT_STACK_SIZE; attr->stackaddr = NULL; attr->sig = _PTHREAD_ATTR_SIG; attr->param.sched_priority = default_priority; attr->param.quantum = 10; /* quantum isn't public yet */ attr->detached = PTHREAD_CREATE_JOINABLE; attr->inherit = _PTHREAD_DEFAULT_INHERITSCHED; attr->policy = _PTHREAD_DEFAULT_POLICY; attr->freeStackOnExit = 1; attr->fastpath = 1; attr->schedset = 0; attr->guardsize = vm_page_size; return (0);}
/* * Set the 'detach' state in a thread attribute structure. * Note: written as a helper function for info hiding */int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate){ if (attr->sig == _PTHREAD_ATTR_SIG) { if ((detachstate == PTHREAD_CREATE_JOINABLE) || (detachstate == PTHREAD_CREATE_DETACHED)) { attr->detached = detachstate; return (0); } else { return (EINVAL); } } else { return (EINVAL); /* Not an attribute structure! */ }}
/* * Set the 'inherit scheduling' state in a thread attribute structure. * Note: written as a helper function for info hiding */int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched){ if (attr->sig == _PTHREAD_ATTR_SIG) { if ((inheritsched == PTHREAD_INHERIT_SCHED) || (inheritsched == PTHREAD_EXPLICIT_SCHED)) { attr->inherit = inheritsched; return (0); } else { return (EINVAL); } } else { return (EINVAL); /* Not an attribute structure! */ }}
/* * Set the scheduling paramters in a thread attribute structure. * Note: written as a helper function for info hiding */int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param){ if (attr->sig == _PTHREAD_ATTR_SIG) { /* TODO: Validate sched_param fields */ attr->param = *param; attr->schedset = 1; return (0); } else { return (EINVAL); /* Not an attribute structure! */ }}
/* * Set the scheduling policy in a thread attribute structure. * Note: written as a helper function for info hiding */int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy){ if (attr->sig == _PTHREAD_ATTR_SIG) { if ((policy == SCHED_OTHER) || (policy == SCHED_RR) || (policy == SCHED_FIFO)) { attr->policy = policy; attr->schedset = 1; return (0); } else { return (EINVAL); } } else { return (EINVAL); /* Not an attribute structure! */ }}
/* * Set the scope for the thread. * We currently only provide PTHREAD_SCOPE_SYSTEM */intpthread_attr_setscope(pthread_attr_t *attr, int scope){ if (attr->sig == _PTHREAD_ATTR_SIG) { if (scope == PTHREAD_SCOPE_SYSTEM) { /* No attribute yet for the scope */ return (0); } else if (scope == PTHREAD_SCOPE_PROCESS) { return (ENOTSUP); } } return (EINVAL); /* Not an attribute structure! */}
/* * Get the scope for the thread. * We currently only provide PTHREAD_SCOPE_SYSTEM */intpthread_attr_getscope(const pthread_attr_t *attr, int *scope){ if (attr->sig == _PTHREAD_ATTR_SIG) { *scope = PTHREAD_SCOPE_SYSTEM; return (0); } return (EINVAL); /* Not an attribute structure! */}
/* Get the base stack address of the given thread */intpthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr){ if (attr->sig == _PTHREAD_ATTR_SIG) { *stackaddr = attr->stackaddr; return (0); } else { return (EINVAL); /* Not an attribute structure! */ }}
intpthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr){ if ((attr->sig == _PTHREAD_ATTR_SIG) && (((uintptr_t)stackaddr % vm_page_size) == 0)) { attr->stackaddr = stackaddr; attr->freeStackOnExit = 0; attr->fastpath = 0; return (0); } else { return (EINVAL); /* Not an attribute structure! */ }}
intpthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize){ if (attr->sig == _PTHREAD_ATTR_SIG) { *stacksize = attr->stacksize; return (0); } else { return (EINVAL); /* Not an attribute structure! */ }}
intpthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize){ if ((attr->sig == _PTHREAD_ATTR_SIG) && ((stacksize % vm_page_size) == 0) && (stacksize >= PTHREAD_STACK_MIN)) { attr->stacksize = stacksize; return (0); } else { return (EINVAL); /* Not an attribute structure! */ }}
intpthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t * stacksize){ if (attr->sig == _PTHREAD_ATTR_SIG) { *stackaddr = (void *)((uintptr_t)attr->stackaddr - attr->stacksize); *stacksize = attr->stacksize; return (0); } else { return (EINVAL); /* Not an attribute structure! */ }}
/* By SUSV spec, the stackaddr is the base address, the lowest addressable * byte address. This is not the same as in pthread_attr_setstackaddr. */intpthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize){ if ((attr->sig == _PTHREAD_ATTR_SIG) && (((uintptr_t)stackaddr % vm_page_size) == 0) && ((stacksize % vm_page_size) == 0) && (stacksize >= PTHREAD_STACK_MIN)) { attr->stackaddr = (void *)((uintptr_t)stackaddr + stacksize); attr->stacksize = stacksize; attr->freeStackOnExit = 0; attr->fastpath = 0; return (0); } else { return (EINVAL); /* Not an attribute structure! */ }}
/* * Set the guardsize attribute in the attr. */intpthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize){ if (attr->sig == _PTHREAD_ATTR_SIG) { /* Guardsize of 0 is valid, ot means no guard */ if ((guardsize % vm_page_size) == 0) { attr->guardsize = guardsize; attr->fastpath = 0; return (0); } else return(EINVAL); } return (EINVAL); /* Not an attribute structure! */}
/* * Get the guardsize attribute in the attr. */intpthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize){ if (attr->sig == _PTHREAD_ATTR_SIG) { *guardsize = attr->guardsize; return (0); } return (EINVAL); /* Not an attribute structure! */}
/* * Create and start execution of a new thread. */
static void_pthread_body(pthread_t self){ _pthread_set_self(self);#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) if( (self->thread_id = __thread_selfid()) == (__uint64_t)-1) printf("Failed to set thread_id in _pthread_body\n");#endif _pthread_exit(self, (self->fun)(self->arg));}
void_pthread_start(pthread_t self, mach_port_t kport, void *(*fun)(void *), void * funarg, size_t stacksize, unsigned int pflags){#if WQ_DEBUG pthread_t pself;#endif pthread_attr_t *attrs = &_pthread_attr_default; char * stackaddr;
if ((pflags & PTHREAD_START_CUSTOM) == 0) { stackaddr = (char *)self; _pthread_struct_init(self, attrs, stackaddr, stacksize, 1, 1);#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) _pthread_set_self(self);#endif LOCK(_pthread_list_lock); if (pflags & PTHREAD_START_SETSCHED) { self->policy = ((pflags >> PTHREAD_START_POLICY_BITSHIFT) & PTHREAD_START_POLICY_MASK); self->param.sched_priority = (pflags & PTHREAD_START_IMPORTANCE_MASK); } /* These are not joinable threads */ if ((pflags & PTHREAD_START_DETACHED) == PTHREAD_START_DETACHED) { self->detached &= ~PTHREAD_CREATE_JOINABLE; self->detached |= PTHREAD_CREATE_DETACHED; } } else { #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) _pthread_set_self(self);#endif LOCK(_pthread_list_lock); } self->kernel_thread = kport; self->fun = fun; self->arg = funarg; /* Add to the pthread list */ if (self->parentcheck == 0) { TAILQ_INSERT_TAIL(&__pthread_head, self, plist);#if PTH_LISTTRACE __kdebug_trace(0x900000c, self, 0, 0, 3, 0);#endif _pthread_count++; } self->childrun = 1; UNLOCK(_pthread_list_lock);
#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) if( (self->thread_id = __thread_selfid()) == (__uint64_t)-1) printf("Failed to set thread_id in pthread_start\n");#endif
#if WQ_DEBUG pself = pthread_self(); if (self != pself) LIBC_ABORT("self %p != pself %p", self, pself);#endif#if PTH_TRACE __kdebug_trace(0x9000030, self, pflags, 0, 0, 0);#endif
_pthread_exit(self, (self->fun)(self->arg));}
int_pthread_create(pthread_t t, const pthread_attr_t *attrs, void *stack, const mach_port_t kernel_thread){ int res; res = 0; do { memset(t, 0, sizeof(*t)); t->newstyle = 0; t->schedset = 0; t->kernalloc = 0; t->tsd[0] = t; t->max_tsd_key = 0; t->wqthread = 0; t->cur_workq = 0; t->cur_workitem = 0; t->stacksize = attrs->stacksize; t->stackaddr = (void *)stack; t->guardsize = attrs->guardsize; t->kernel_thread = kernel_thread; t->detached = attrs->detached; t->inherit = attrs->inherit; t->policy = attrs->policy; t->param = attrs->param; t->freeStackOnExit = attrs->freeStackOnExit; t->cancel_error = 0; t->sig = _PTHREAD_SIG; t->reply_port = MACH_PORT_NULL; t->cthread_self = NULL; LOCK_INIT(t->lock); t->plist.tqe_next = (struct _pthread *)0; t->plist.tqe_prev = (struct _pthread **)0; t->cancel_state = PTHREAD_CANCEL_ENABLE | PTHREAD_CANCEL_DEFERRED; t->__cleanup_stack = (struct __darwin_pthread_handler_rec *)NULL; t->death = SEMAPHORE_NULL;
if (kernel_thread != MACH_PORT_NULL) (void)pthread_setschedparam_internal(t, kernel_thread, t->policy, &t->param); } while (0); return (res);}
void_pthread_struct_init(pthread_t t, const pthread_attr_t *attrs, void * stack, size_t stacksize, int kernalloc, int nozero){ mach_vm_offset_t stackaddr = (mach_vm_offset_t)(uintptr_t)stack;
if (nozero == 0) { memset(t, 0, sizeof(*t)); t->plist.tqe_next = (struct _pthread *)0; t->plist.tqe_prev = (struct _pthread **)0; } t->schedset = attrs->schedset; t->tsd[0] = t; if (kernalloc != 0) { stackaddr = (mach_vm_offset_t)(uintptr_t)t;
/* if allocated from kernel set values appropriately */ t->stacksize = stacksize; t->stackaddr = (void *)(uintptr_t)stackaddr; t->freeStackOnExit = 1; t->freeaddr = (void *)(uintptr_t)(stackaddr - stacksize - vm_page_size); t->freesize = pthreadsize + stacksize + vm_page_size; } else { t->stacksize = attrs->stacksize; t->stackaddr = (void *)stack; } t->guardsize = attrs->guardsize; t->detached = attrs->detached; t->inherit = attrs->inherit; t->policy = attrs->policy; t->param = attrs->param; t->cancel_error = 0; t->sig = _PTHREAD_SIG; t->reply_port = MACH_PORT_NULL; t->cthread_self = NULL; LOCK_INIT(t->lock); t->cancel_state = PTHREAD_CANCEL_ENABLE | PTHREAD_CANCEL_DEFERRED; t->__cleanup_stack = (struct __darwin_pthread_handler_rec *)NULL; t->death = SEMAPHORE_NULL; t->newstyle = 1; t->kernalloc = kernalloc; t->wqthread = 0; t->cur_workq = 0; t->cur_workitem = 0; t->max_tsd_key = 0;}
/* Need to deprecate this in future */int_pthread_is_threaded(void){ return __is_threaded;}
/* Non portable public api to know whether this process has(had) atleast one thread * apart from main thread. There could be race if there is a thread in the process of * creation at the time of call . It does not tell whether there are more than one thread * at this point of time. */intpthread_is_threaded_np(void){ return (__is_threaded);}
mach_port_tpthread_mach_thread_np(pthread_t t){ mach_port_t kport = MACH_PORT_NULL;
if (t == NULL) goto out; /* * If the call is on self, return the kernel port. We cannot * add this bypass for main thread as it might have exited, * and we should not return stale port info. */ if (t == pthread_self()) { kport = t->kernel_thread; goto out; } if (_pthread_lookup_thread(t, &kport, 0) != 0) return((mach_port_t)0);
out: return(kport);}
pthread_t pthread_from_mach_thread_np(mach_port_t kernel_thread) { struct _pthread * p = NULL;
/* No need to wait as mach port is already known */ LOCK(_pthread_list_lock); TAILQ_FOREACH(p, &__pthread_head, plist) { if (p->kernel_thread == kernel_thread) break; } UNLOCK(_pthread_list_lock); return p;}
size_tpthread_get_stacksize_np(pthread_t t){ int ret; size_t size = 0;
if (t == NULL) return(ESRCH); if ( t == pthread_self() || t == &_thread ) //since the main thread will not get de-allocated from underneath us { size=t->stacksize; return size; }
LOCK(_pthread_list_lock);
if ((ret = _pthread_find_thread(t)) != 0) { UNLOCK(_pthread_list_lock); return(ret); }
size=t->stacksize; UNLOCK(_pthread_list_lock); return(size);}
void *pthread_get_stackaddr_np(pthread_t t){ int ret; void * addr = NULL;
if (t == NULL) return((void *)(uintptr_t)ESRCH); if(t == pthread_self() || t == &_thread) //since the main thread will not get deallocated from underneath us return t->stackaddr;
LOCK(_pthread_list_lock);
if ((ret = _pthread_find_thread(t)) != 0) { UNLOCK(_pthread_list_lock); return((void *)(uintptr_t)ret); } addr = t->stackaddr; UNLOCK(_pthread_list_lock);
return(addr);}
mach_port_t_pthread_reply_port(pthread_t t){ return t->reply_port;}
/* returns non-zero if the current thread is the main thread */intpthread_main_np(void){ pthread_t self = pthread_self();
return ((self->detached & _PTHREAD_CREATE_PARENT) == _PTHREAD_CREATE_PARENT);}
#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) /* if we are passed in a pthread_t that is NULL, then we return the current thread's thread_id. So folks don't have to call pthread_self, in addition to us doing it, if they just want their thread_id.*/intpthread_threadid_np(pthread_t thread, __uint64_t *thread_id){ int rval=0; pthread_t self = pthread_self();
if (thread_id == NULL) { return(EINVAL); } else if (thread == NULL || thread == self) { *thread_id = self->thread_id; return rval; } LOCK(_pthread_list_lock); if ((rval = _pthread_find_thread(thread)) != 0) { UNLOCK(_pthread_list_lock); return(rval); } *thread_id = thread->thread_id; UNLOCK(_pthread_list_lock); return rval;}#endif
intpthread_getname_np(pthread_t thread, char *threadname, size_t len){ int rval; rval = 0;
if (thread == NULL) return(ESRCH); LOCK(_pthread_list_lock); if ((rval = _pthread_find_thread(thread)) != 0) { UNLOCK(_pthread_list_lock); return(rval); } strlcpy(threadname, thread->pthread_name, len); UNLOCK(_pthread_list_lock); return rval;}
intpthread_setname_np(const char *threadname){ int rval = 0; int len = 0; pthread_t current_thread = pthread_self();
if (threadname != NULL) len = strlen(threadname);
/* protytype is in pthread_internals.h */ rval = proc_setthreadname((void *)threadname, len); if (rval == 0) { if (threadname != NULL) { strlcpy(current_thread->pthread_name, threadname, MAXTHREADNAMESIZE); } else { memset(current_thread->pthread_name, 0 , MAXTHREADNAMESIZE); }
} return rval;
}
static int _new_pthread_create_suspended(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg, int create_susp){ pthread_attr_t *attrs; void *stack; int error; unsigned int flags; pthread_t t,t2; kern_return_t kern_res; mach_port_t kernel_thread = MACH_PORT_NULL; int needresume; task_t self = mach_task_self(); int kernalloc = 0; int susp = create_susp;
if ((attrs = (pthread_attr_t *)attr) == (pthread_attr_t *)NULL) { /* Set up default paramters */ attrs = &_pthread_attr_default; } else if (attrs->sig != _PTHREAD_ATTR_SIG) { return EINVAL; } error = 0;
if (((attrs->policy != _PTHREAD_DEFAULT_POLICY) || (attrs->param.sched_priority != default_priority)) && (create_susp == 0)) { needresume = 1; susp = 1; } else needresume = 0;
/* In default policy (ie SCHED_OTHER) only sched_priority is used. Check for * any change in priority or policy is needed here. */ if ((__oldstyle == 1) || (create_susp != 0)) { /* Rosetta or pthread_create_suspended() */ /* running under rosetta */ /* Allocate a stack for the thread */#if PTH_TRACE __kdebug_trace(0x9000000, create_susp, 0, 0, 0, 0);#endif if ((error = _pthread_allocate_stack(attrs, &stack)) != 0) { return(error); } t = (pthread_t)malloc(sizeof(struct _pthread)); *thread = t; if (susp) { /* Create the Mach thread for this thread */ PTHREAD_MACH_CALL(thread_create(self, &kernel_thread), kern_res); if (kern_res != KERN_SUCCESS) { printf("Can't create thread: %d\n", kern_res); return(EINVAL); } } if ((error = _pthread_create(t, attrs, stack, kernel_thread)) != 0) { return(error); } set_malloc_singlethreaded(0); __is_threaded = 1;
/* Send it on it's way */ t->arg = arg; t->fun = start_routine; t->newstyle = 0; /* Now set it up to execute */ LOCK(_pthread_list_lock); TAILQ_INSERT_TAIL(&__pthread_head, t, plist);#if PTH_LISTTRACE __kdebug_trace(0x900000c, t, 0, 0, 4, 0);#endif _pthread_count++; UNLOCK(_pthread_list_lock); _pthread_setup(t, _pthread_body, stack, susp, needresume); return(0); } else {
flags = 0; if (attrs->fastpath == 1) kernalloc = 1;
if (attrs->detached == PTHREAD_CREATE_DETACHED) flags |= PTHREAD_START_DETACHED; if (attrs->schedset != 0) { flags |= PTHREAD_START_SETSCHED; flags |= ((attrs->policy & PTHREAD_START_POLICY_MASK) << PTHREAD_START_POLICY_BITSHIFT); flags |= (attrs->param.sched_priority & PTHREAD_START_IMPORTANCE_MASK); }
set_malloc_singlethreaded(0); __is_threaded = 1;
if (kernalloc == 0) { /* Allocate a stack for the thread */ flags |= PTHREAD_START_CUSTOM; if ((error = _pthread_create_pthread_onstack(attrs, &stack, &t)) != 0) { return(error); } /* Send it on it's way */ t->arg = arg; t->fun = start_routine; t->newstyle = 1;
#if PTH_TRACE __kdebug_trace(0x9000004, t, flags, 0, 0, 0);#endif if ((t2 = __bsdthread_create(start_routine, arg, stack, t, flags)) == (pthread_t)-1) { _pthread_free_pthread_onstack(t, 1, 0); return (EAGAIN); } else t=t2; LOCK(_pthread_list_lock); t->parentcheck = 1; if ((t->childexit != 0) && ((t->detached & PTHREAD_CREATE_DETACHED) == PTHREAD_CREATE_DETACHED)) { /* detached child exited, mop up */ UNLOCK(_pthread_list_lock);#if PTH_TRACE __kdebug_trace(0x9000008, t, 0, 0, 1, 0);#endif if(t->freeStackOnExit) vm_deallocate(self, (mach_vm_address_t)(uintptr_t)t, pthreadsize); else free(t); } else if (t->childrun == 0) { TAILQ_INSERT_TAIL(&__pthread_head, t, plist); _pthread_count++;#if PTH_LISTTRACE __kdebug_trace(0x900000c, t, 0, 0, 1, 0);#endif UNLOCK(_pthread_list_lock); } else UNLOCK(_pthread_list_lock);
*thread = t;
#if PTH_TRACE __kdebug_trace(0x9000014, t, 0, 0, 1, 0);#endif return (0);
} else { /* kernel allocation */#if PTH_TRACE __kdebug_trace(0x9000018, flags, 0, 0, 0, 0);#endif if ((t = __bsdthread_create(start_routine, arg, (void *)attrs->stacksize, NULL, flags)) == (pthread_t)-1) return (EAGAIN); /* Now set it up to execute */ LOCK(_pthread_list_lock); t->parentcheck = 1; if ((t->childexit != 0) && ((t->detached & PTHREAD_CREATE_DETACHED) == PTHREAD_CREATE_DETACHED)) { /* detached child exited, mop up */ UNLOCK(_pthread_list_lock);#if PTH_TRACE __kdebug_trace(0x9000008, t, pthreadsize, 0, 2, 0);#endif vm_deallocate(self, (mach_vm_address_t)(uintptr_t)t, pthreadsize); } else if (t->childrun == 0) { TAILQ_INSERT_TAIL(&__pthread_head, t, plist); _pthread_count++;#if PTH_LISTTRACE __kdebug_trace(0x900000c, t, 0, 0, 2, 0);#endif UNLOCK(_pthread_list_lock); } else UNLOCK(_pthread_list_lock);
*thread = t;
#if PTH_TRACE __kdebug_trace(0x9000014, t, 0, 0, 2, 0);#endif return(0); } }}
static int _pthread_create_suspended(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg, int suspended){ pthread_attr_t *attrs; void *stack; int res; pthread_t t; kern_return_t kern_res; mach_port_t kernel_thread = MACH_PORT_NULL; int needresume;
if ((attrs = (pthread_attr_t *)attr) == (pthread_attr_t *)NULL) { /* Set up default paramters */ attrs = &_pthread_attr_default; } else if (attrs->sig != _PTHREAD_ATTR_SIG) { return EINVAL; } res = 0;
/* In default policy (ie SCHED_OTHER) only sched_priority is used. Check for * any change in priority or policy is needed here. */ if (((attrs->policy != _PTHREAD_DEFAULT_POLICY) || (attrs->param.sched_priority != default_priority)) && (suspended == 0)) { needresume = 1; suspended = 1; } else needresume = 0;
do { /* Allocate a stack for the thread */ if ((res = _pthread_allocate_stack(attrs, &stack)) != 0) { break; } t = (pthread_t)malloc(sizeof(struct _pthread)); *thread = t; if (suspended) { /* Create the Mach thread for this thread */ PTHREAD_MACH_CALL(thread_create(mach_task_self(), &kernel_thread), kern_res); if (kern_res != KERN_SUCCESS) { printf("Can't create thread: %d\n", kern_res); res = EINVAL; /* Need better error here? */ break; } } if ((res = _pthread_create(t, attrs, stack, kernel_thread)) != 0) { break; } set_malloc_singlethreaded(0); __is_threaded = 1;
/* Send it on it's way */ t->arg = arg; t->fun = start_routine; /* Now set it up to execute */ LOCK(_pthread_list_lock); TAILQ_INSERT_TAIL(&__pthread_head, t, plist);#if PTH_LISTTRACE __kdebug_trace(0x900000c, t, 0, 0, 5, 0);#endif _pthread_count++; UNLOCK(_pthread_list_lock); _pthread_setup(t, _pthread_body, stack, suspended, needresume); } while (0); return (res);}
intpthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg){ return _new_pthread_create_suspended(thread, attr, start_routine, arg, 0);}
intpthread_create_suspended_np(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg){ return _pthread_create_suspended(thread, attr, start_routine, arg, 1);}
/* * Make a thread 'undetached' - no longer 'joinable' with other threads. */int pthread_detach(pthread_t thread){ int newstyle = 0; int ret;
if ((ret = _pthread_lookup_thread(thread, NULL, 1)) != 0) { return (ret); /* Not a valid thread */ }
LOCK(thread->lock); newstyle = thread->newstyle; if (thread->detached & PTHREAD_CREATE_JOINABLE) { if (thread->detached & _PTHREAD_EXITED) { UNLOCK(thread->lock); pthread_join(thread, NULL); return 0; } else { if (newstyle == 0) { semaphore_t death = thread->death;
thread->detached &= ~PTHREAD_CREATE_JOINABLE; thread->detached |= PTHREAD_CREATE_DETACHED; UNLOCK(thread->lock); if (death) (void) semaphore_signal(death); } else { mach_port_t joinport = thread->joiner_notify; thread->detached &= ~PTHREAD_CREATE_JOINABLE; thread->detached |= PTHREAD_CREATE_DETACHED; UNLOCK(thread->lock); if (joinport) { semaphore_signal(joinport); } } return(0); } } else { UNLOCK(thread->lock); return (EINVAL); }}
/* * pthread_kill call to system call */int pthread_kill ( pthread_t th, int sig){ int error = 0; mach_port_t kport = MACH_PORT_NULL; if ((sig < 0) || (sig > NSIG)) return(EINVAL);
if (_pthread_lookup_thread(th, &kport, 0) != 0) return (ESRCH); /* Not a valid thread */
/* if the thread is a workqueue thread, just return error */ if ((th->wqthread != 0) && (th->wqkillset ==0)) { return(ENOTSUP); }
error = __pthread_kill(kport, sig);
if (error == -1) error = errno; return(error);}
int __pthread_workqueue_setkill(int enable){ pthread_t self = pthread_self();
LOCK(self->lock); if (enable == 0) self->wqkillset = 0; else self->wqkillset = 1; UNLOCK(self->lock);
return(0);
}
/* Announce that there are pthread resources ready to be reclaimed in a *//* subsequent pthread_exit or reaped by pthread_join. In either case, the Mach *//* thread underneath is terminated right away. */staticvoid _pthread_become_available(pthread_t thread, mach_port_t kernel_thread) { pthread_reap_msg_t msg; kern_return_t ret;
msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, MACH_MSG_TYPE_MOVE_SEND); msg.header.msgh_size = sizeof msg - sizeof msg.trailer; msg.header.msgh_remote_port = thread_recycle_port; msg.header.msgh_local_port = kernel_thread; msg.header.msgh_id = 0x44454144; /* 'DEAD' */ msg.thread = thread; ret = mach_msg_send(&msg.header); assert(ret == MACH_MSG_SUCCESS);}
/* Reap the resources for available threads */__private_extern__int _pthread_reap_thread(pthread_t th, mach_port_t kernel_thread, void **value_ptr, int conforming) { mach_port_type_t ptype; kern_return_t ret; task_t self;
self = mach_task_self(); if (kernel_thread != MACH_PORT_DEAD) { ret = mach_port_type(self, kernel_thread, &ptype); if (ret == KERN_SUCCESS && ptype != MACH_PORT_TYPE_DEAD_NAME) { /* not quite dead yet... */ return EAGAIN; } ret = mach_port_deallocate(self, kernel_thread); if (ret != KERN_SUCCESS) { fprintf(stderr, "mach_port_deallocate(kernel_thread) failed: %s\n", mach_error_string(ret)); } }
if (th->reply_port != MACH_PORT_NULL) { ret = mach_port_mod_refs(self, th->reply_port, MACH_PORT_RIGHT_RECEIVE, -1); if (ret != KERN_SUCCESS) { fprintf(stderr, "mach_port_mod_refs(reply_port) failed: %s\n", mach_error_string(ret)); } }
if (th->freeStackOnExit) { vm_address_t addr = (vm_address_t)th->stackaddr; vm_size_t size;
size = (vm_size_t)th->stacksize + th->guardsize; addr -= size; ret = vm_deallocate(self, addr, size); if (ret != KERN_SUCCESS) { fprintf(stderr, "vm_deallocate(stack) failed: %s\n", mach_error_string(ret)); } }
if (value_ptr) *value_ptr = th->exit_value; if (conforming) { if ((th->cancel_state & (PTHREAD_CANCEL_ENABLE|_PTHREAD_CANCEL_PENDING)) == (PTHREAD_CANCEL_ENABLE|_PTHREAD_CANCEL_PENDING) && (value_ptr != NULL)) *value_ptr = PTHREAD_CANCELED; th->sig = _PTHREAD_NO_SIG; }
if (th != &_thread) free(th);
return 0;}
staticvoid _pthread_reap_threads(void){ pthread_reap_msg_t msg; kern_return_t ret;
ret = mach_msg(&msg.header, MACH_RCV_MSG|MACH_RCV_TIMEOUT, 0, sizeof msg, thread_recycle_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); while (ret == MACH_MSG_SUCCESS) { mach_port_t kernel_thread = msg.header.msgh_remote_port; pthread_t thread = msg.thread;
/* deal with race with thread_create_running() */ if (kernel_thread == MACH_PORT_NULL && kernel_thread != thread->kernel_thread) { kernel_thread = thread->kernel_thread; } if ( kernel_thread == MACH_PORT_NULL || _pthread_reap_thread(thread, kernel_thread, (void **)0, 0) == EAGAIN) { /* not dead yet, put it back for someone else to reap, stop here */ _pthread_become_available(thread, kernel_thread); return; }
ret = mach_msg(&msg.header, MACH_RCV_MSG|MACH_RCV_TIMEOUT, 0, sizeof msg, thread_recycle_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); }}
/* For compatibility... */
pthread_t_pthread_self() { return pthread_self();}
/* * Terminate a thread. */int __disable_threadsignal(int);
static void _pthread_exit(pthread_t self, void *value_ptr){ struct __darwin_pthread_handler_rec *handler; kern_return_t kern_res; int thread_count; int newstyle = self->newstyle;
/* Make this thread not to receive any signals */ __disable_threadsignal(1);
#if PTH_TRACE __kdebug_trace(0x900001c, self, newstyle, 0, 0, 0);#endif
/* set cancel state to disable and type to deferred */ _pthread_setcancelstate_exit(self, value_ptr, __unix_conforming);
while ((handler = self->__cleanup_stack) != 0) { (handler->__routine)(handler->__arg); self->__cleanup_stack = handler->__next; } _pthread_tsd_cleanup(self);
if (newstyle == 0) { _pthread_reap_threads();
LOCK(self->lock); self->detached |= _PTHREAD_EXITED;
if (self->detached & PTHREAD_CREATE_JOINABLE) { mach_port_t death = self->death; self->exit_value = value_ptr; UNLOCK(self->lock); /* the joiner will need a kernel thread reference, leave ours for it */ if (death) { PTHREAD_MACH_CALL(semaphore_signal(death), kern_res); if (kern_res != KERN_SUCCESS) fprintf(stderr, "semaphore_signal(death) failed: %s\n", mach_error_string(kern_res)); } LOCK(_pthread_list_lock); thread_count = --_pthread_count; UNLOCK(_pthread_list_lock); } else { UNLOCK(self->lock); LOCK(_pthread_list_lock); TAILQ_REMOVE(&__pthread_head, self, plist);#if PTH_LISTTRACE __kdebug_trace(0x9000010, self, 0, 0, 5, 0);#endif thread_count = --_pthread_count; UNLOCK(_pthread_list_lock); /* with no joiner, we let become available consume our cached ref */ _pthread_become_available(self, self->kernel_thread); }
if (thread_count <= 0) exit(0);
/* Use a new reference to terminate ourselves. Should never return. */ PTHREAD_MACH_CALL(thread_terminate(mach_thread_self()), kern_res); fprintf(stderr, "thread_terminate(mach_thread_self()) failed: %s\n", mach_error_string(kern_res)); } else { semaphore_t joinsem = SEMAPHORE_NULL;
if ((self->joiner_notify == (mach_port_t)0) && (self->detached & PTHREAD_CREATE_JOINABLE)) joinsem = new_sem_from_pool(); LOCK(self->lock); self->detached |= _PTHREAD_EXITED;
self->exit_value = value_ptr; if (self->detached & PTHREAD_CREATE_JOINABLE) { if (self->joiner_notify == (mach_port_t)0) { self->joiner_notify = joinsem; joinsem = SEMAPHORE_NULL; } UNLOCK(self->lock); if (joinsem != SEMAPHORE_NULL) restore_sem_to_pool(joinsem); _pthread_free_pthread_onstack(self, 0, 1); } else { UNLOCK(self->lock); /* with no joiner, we let become available consume our cached ref */ if (joinsem != SEMAPHORE_NULL) restore_sem_to_pool(joinsem); _pthread_free_pthread_onstack(self, 1, 1); } } LIBC_ABORT("thread %p didn't exit", self);}
voidpthread_exit(void *value_ptr){ pthread_t self = pthread_self(); /* if the current thread is a workqueue thread, just crash the app, as per libdispatch folks */ if (self->wqthread == 0) { _pthread_exit(self, value_ptr); } else { LIBC_ABORT("pthread_exit() may only be called against threads created via pthread_create()"); }}
/* * Get the scheduling policy and scheduling paramters for a thread. */int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param){ int ret;
if (thread == NULL) return(ESRCH); LOCK(_pthread_list_lock);
if ((ret = _pthread_find_thread(thread)) != 0) { UNLOCK(_pthread_list_lock); return(ret); } if (policy != 0) *policy = thread->policy; if (param != 0) *param = thread->param; UNLOCK(_pthread_list_lock);
return(0);}
/* * Set the scheduling policy and scheduling paramters for a thread. */static int pthread_setschedparam_internal(pthread_t thread, mach_port_t kport, int policy, const struct sched_param *param){ policy_base_data_t bases; policy_base_t base; mach_msg_type_number_t count; kern_return_t ret;
switch (policy) { case SCHED_OTHER: bases.ts.base_priority = param->sched_priority; base = (policy_base_t)&bases.ts; count = POLICY_TIMESHARE_BASE_COUNT; break; case SCHED_FIFO: bases.fifo.base_priority = param->sched_priority; base = (policy_base_t)&bases.fifo; count = POLICY_FIFO_BASE_COUNT; break; case SCHED_RR: bases.rr.base_priority = param->sched_priority; /* quantum isn't public yet */ bases.rr.quantum = param->quantum; base = (policy_base_t)&bases.rr; count = POLICY_RR_BASE_COUNT; break; default: return (EINVAL); } ret = thread_policy(kport, policy, base, count, TRUE); if (ret != KERN_SUCCESS) return (EINVAL); return (0);}
int pthread_setschedparam(pthread_t t, int policy, const struct sched_param *param){ mach_port_t kport = MACH_PORT_NULL; int error; int bypass = 1;
if (t != pthread_self() && t != &_thread ) { //since the main thread will not get de-allocated from underneath us bypass = 0; if (_pthread_lookup_thread(t, &kport, 0) != 0) return(ESRCH); } else kport = t->kernel_thread;
error = pthread_setschedparam_internal(t, kport, policy, param); if (error == 0) { if (bypass == 0) { /* ensure the thread is still valid */ LOCK(_pthread_list_lock); if ((error = _pthread_find_thread(t)) != 0) { UNLOCK(_pthread_list_lock); return(error); } t->policy = policy; t->param = *param; UNLOCK(_pthread_list_lock); } else { t->policy = policy; t->param = *param; } } return(error);}
/* * Get the minimum priority for the given policy */intsched_get_priority_min(int policy){ return default_priority - 16;}
/* * Get the maximum priority for the given policy */intsched_get_priority_max(int policy){ return default_priority + 16;}
/* * Determine if two thread identifiers represent the same thread. */int pthread_equal(pthread_t t1, pthread_t t2){ return (t1 == t2);}
// Force LLVM not to optimise this to a call to __pthread_set_self, if it does// then _pthread_set_self won't be bound when secondary threads try and start up.void __attribute__((noinline))_pthread_set_self(pthread_t p){ extern void __pthread_set_self(void *);
if (p == 0) { if (_thread.tsd[0] != 0) { bzero(&_thread, sizeof(struct _pthread)); } p = &_thread; } p->tsd[0] = p; __pthread_set_self(&p->tsd[0]);}
void cthread_set_self(void *cself){ pthread_t self = pthread_self(); if ((self == (pthread_t)NULL) || (self->sig != _PTHREAD_SIG)) { _pthread_set_self(cself); return; } self->cthread_self = cself;}
void *ur_cthread_self(void) { pthread_t self = pthread_self(); if ((self == (pthread_t)NULL) || (self->sig != _PTHREAD_SIG)) { return (void *)self; } return self->cthread_self;}
/* * cancellation handler for pthread once as the init routine can have a * cancellation point. In that case we need to restore the spin unlock */void__pthread_once_cancel_handler(pthread_once_t *once_control){ _spin_unlock(&once_control->lock);}
/* * Execute a function exactly one time in a thread-safe fashion. */int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)){ _spin_lock(&once_control->lock); if (once_control->sig == _PTHREAD_ONCE_SIG_init) { pthread_cleanup_push((void (*)(void *))__pthread_once_cancel_handler, once_control); (*init_routine)(); pthread_cleanup_pop(0); once_control->sig = _PTHREAD_ONCE_SIG; } _spin_unlock(&once_control->lock); return (0); /* Spec defines no possible errors! */}
/* * Insert a cancellation point in a thread. *//*__private_extern__*/ void // accessed by libsystem.dylib_pthread_testcancel(pthread_t thread, int isconforming){ LOCK(thread->lock); if ((thread->cancel_state & (PTHREAD_CANCEL_ENABLE|_PTHREAD_CANCEL_PENDING)) == (PTHREAD_CANCEL_ENABLE|_PTHREAD_CANCEL_PENDING)) { UNLOCK(thread->lock); if (isconforming) pthread_exit(PTHREAD_CANCELED); else pthread_exit(0); } UNLOCK(thread->lock);}
intpthread_getconcurrency(void){ return(pthread_concurrency);}
intpthread_setconcurrency(int new_level){ if (new_level < 0) return EINVAL; pthread_concurrency = new_level; return(0);}
/* * Perform package initialization - called automatically when application starts */intpthread_init(void){ pthread_attr_t *attrs; pthread_t thread; kern_return_t kr; host_priority_info_data_t priority_info; host_info_t info; host_flavor_t flavor; host_t host; mach_msg_type_number_t count; int mib[2]; int ncpus = 0; size_t len; void *stackaddr;
pthreadsize = round_page(sizeof (struct _pthread)); count = HOST_PRIORITY_INFO_COUNT; info = (host_info_t)&priority_info; flavor = HOST_PRIORITY_INFO; host = mach_host_self(); kr = host_info(host, flavor, info, &count); if (kr != KERN_SUCCESS) printf("host_info failed (%d); probably need privilege.\n", kr); else { default_priority = priority_info.user_priority; min_priority = priority_info.minimum_priority; max_priority = priority_info.maximum_priority; } attrs = &_pthread_attr_default; pthread_attr_init(attrs);
TAILQ_INIT(&__pthread_head); LOCK_INIT(_pthread_list_lock); thread = &_thread; TAILQ_INSERT_HEAD(&__pthread_head, thread, plist); _pthread_set_self(thread);#if PTH_LISTTRACE __kdebug_trace(0x900000c, thread, 0, 0, 10, 0);#endif /* In case of dyld reset the tsd keys from 1 - 10 */ _pthread_keys_init();
mib[0] = CTL_KERN; mib[1] = KERN_USRSTACK; len = sizeof (stackaddr); if (sysctl (mib, 2, &stackaddr, &len, NULL, 0) != 0) stackaddr = (void *)USRSTACK; _pthread_create(thread, attrs, stackaddr, mach_thread_self()); thread->stacksize = DFLSSIZ; //initialize main thread's stacksize based on vmparam.h thread->detached = PTHREAD_CREATE_JOINABLE|_PTHREAD_CREATE_PARENT;
_init_cpu_capabilities(); if ((ncpus = _NumCPUs()) > 1) _spin_tries = MP_SPIN_TRIES;
workq_targetconc[WORKQ_HIGH_PRIOQUEUE] = ncpus; workq_targetconc[WORKQ_DEFAULT_PRIOQUEUE] = ncpus; workq_targetconc[WORKQ_LOW_PRIOQUEUE] = ncpus; workq_targetconc[WORKQ_BG_PRIOQUEUE] = ncpus;
mach_port_deallocate(mach_task_self(), host); #if defined(__ppc__) IF_ROSETTA() { __oldstyle = 1; }#endif
#if defined(_OBJC_PAGE_BASE_ADDRESS){ vm_address_t objcRTPage = (vm_address_t)_OBJC_PAGE_BASE_ADDRESS; kr = vm_map(mach_task_self(), &objcRTPage, vm_page_size * 4, vm_page_size - 1, VM_FLAGS_FIXED | VM_MAKE_TAG(0), // Which tag to use? MACH_PORT_NULL, (vm_address_t)0, FALSE, (vm_prot_t)0, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE, VM_INHERIT_DEFAULT); /* We ignore the return result here. The ObjC runtime will just have to deal. */}#endif //added so that thread_recycle_port is initialized on new launch. _pthread_fork_child_postinit(); mig_init(1); /* enable multi-threaded mig interfaces */ if (__oldstyle == 0) {#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) __bsdthread_register(thread_start, start_wqthread, round_page(sizeof(struct _pthread)), _pthread_start, &workq_targetconc[0], (uintptr_t)(&thread->tsd[__PTK_LIBDISPATCH_KEY0]) - (uintptr_t)(&thread->tsd[0]));#else __bsdthread_register(_pthread_start, _pthread_wqthread, round_page(sizeof(struct _pthread)), NULL, &workq_targetconc[0], (uintptr_t)&thread->tsd[__PTK_LIBDISPATCH_KEY0] - (uintptr_t)thread);#endif }
#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) if( (thread->thread_id = __thread_selfid()) == (__uint64_t)-1) printf("Failed to set thread_id in pthread_init\n");#endif return 0;}
int sched_yield(void){ swtch_pri(0); return 0;}
/* This used to be the "magic" that gets the initialization routine called when the application starts *//* * (These has been moved to setenv.c, so we can use it to fix a less than 10.5 * crt1.o issue) * static int _do_nothing(void) { return 0; } * int (*_cthread_init_routine)(void) = _do_nothing; */
/* Get a semaphore from the pool, growing it if necessary */
__private_extern__ semaphore_t new_sem_from_pool(void) { kern_return_t res; semaphore_t sem; int i; LOCK(sem_pool_lock); if (sem_pool_current == sem_pool_count) { sem_pool_count += 16; sem_pool = realloc(sem_pool, sem_pool_count * sizeof(semaphore_t)); for (i = sem_pool_current; i < sem_pool_count; i++) { PTHREAD_MACH_CALL(semaphore_create(mach_task_self(), &sem_pool[i], SYNC_POLICY_FIFO, 0), res); } } sem = sem_pool[sem_pool_current++]; UNLOCK(sem_pool_lock); return sem;}
/* Put a semaphore back into the pool */__private_extern__ void restore_sem_to_pool(semaphore_t sem) { LOCK(sem_pool_lock); sem_pool[--sem_pool_current] = sem; UNLOCK(sem_pool_lock);}
static void sem_pool_reset(void) { LOCK(sem_pool_lock); sem_pool_count = 0; sem_pool_current = 0; sem_pool = NULL; UNLOCK(sem_pool_lock);}
__private_extern__ void _pthread_fork_child(pthread_t p) { /* Just in case somebody had it locked... */ UNLOCK(sem_pool_lock); sem_pool_reset(); /* No need to hold the pthread_list_lock as no one other than this * thread is present at this time */ TAILQ_INIT(&__pthread_head); LOCK_INIT(_pthread_list_lock); TAILQ_INSERT_HEAD(&__pthread_head, p, plist);#if PTH_LISTTRACE __kdebug_trace(0x900000c, p, 0, 0, 10, 0);#endif _pthread_count = 1;#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) if( (p->thread_id = __thread_selfid()) == (__uint64_t)-1) printf("Failed to set thread_id in pthread_fork_child\n");#endif}
void _pthread_fork_child_postinit() { kern_return_t kr;
kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &thread_recycle_port); if (kr != KERN_SUCCESS) { abort(); }}
/* * Query/update the cancelability 'state' of a thread */int_pthread_setcancelstate_internal(int state, int *oldstate, int conforming){ pthread_t self = pthread_self();
switch (state) { case PTHREAD_CANCEL_ENABLE: if (conforming) __pthread_canceled(1); break; case PTHREAD_CANCEL_DISABLE: if (conforming) __pthread_canceled(2); break; default: return EINVAL; }
self = pthread_self(); LOCK(self->lock); if (oldstate) *oldstate = self->cancel_state & _PTHREAD_CANCEL_STATE_MASK; self->cancel_state &= ~_PTHREAD_CANCEL_STATE_MASK; self->cancel_state |= state; UNLOCK(self->lock); if (!conforming) _pthread_testcancel(self, 0); /* See if we need to 'die' now... */ return (0);}
/* When a thread exits set the cancellation state to DISABLE and DEFERRED */static void_pthread_setcancelstate_exit(pthread_t self, void * value_ptr, int conforming){ LOCK(self->lock); self->cancel_state &= ~(_PTHREAD_CANCEL_STATE_MASK | _PTHREAD_CANCEL_TYPE_MASK); self->cancel_state |= (PTHREAD_CANCEL_DISABLE | PTHREAD_CANCEL_DEFERRED); if ((value_ptr == PTHREAD_CANCELED)) {// 4597450: begin self->detached |= _PTHREAD_WASCANCEL;// 4597450: end } UNLOCK(self->lock);}
int_pthread_join_cleanup(pthread_t thread, void ** value_ptr, int conforming){ kern_return_t res; int ret;
#if PTH_TRACE __kdebug_trace(0x9000028, thread, 0, 0, 1, 0);#endif /* The scenario where the joiner was waiting for the thread and * the pthread detach happened on that thread. Then the semaphore * will trigger but by the time joiner runs, the target thread could be * freed. So we need to make sure that the thread is still in the list * and is joinable before we continue with the join. */ LOCK(_pthread_list_lock); if ((ret = _pthread_find_thread(thread)) != 0) { UNLOCK(_pthread_list_lock); /* returns ESRCH */ return(ret); } if ((thread->detached & PTHREAD_CREATE_JOINABLE) == 0) { /* the thread might be a detached thread */ UNLOCK(_pthread_list_lock); return(ESRCH);
} /* It is still a joinable thread and needs to be reaped */ TAILQ_REMOVE(&__pthread_head, thread, plist);#if PTH_LISTTRACE __kdebug_trace(0x9000010, thread, 0, 0, 3, 0);#endif UNLOCK(_pthread_list_lock);
if (value_ptr) *value_ptr = thread->exit_value; if (conforming) { if ((thread->cancel_state & (PTHREAD_CANCEL_ENABLE|_PTHREAD_CANCEL_PENDING)) == (PTHREAD_CANCEL_ENABLE|_PTHREAD_CANCEL_PENDING) && (value_ptr != NULL)) { *value_ptr = PTHREAD_CANCELED; } } if (thread->reply_port != MACH_PORT_NULL) { res = mach_port_mod_refs(mach_task_self(), thread->reply_port, MACH_PORT_RIGHT_RECEIVE, -1); if (res != KERN_SUCCESS) fprintf(stderr,"mach_port_mod_refs(reply_port) failed: %s\n",mach_error_string(res)); thread->reply_port = MACH_PORT_NULL; } if (thread->freeStackOnExit) { thread->sig = _PTHREAD_NO_SIG;#if PTH_TRACE __kdebug_trace(0x9000028, thread, 0, 0, 2, 0);#endif vm_deallocate(mach_task_self(), (mach_vm_address_t)(uintptr_t)thread, pthreadsize); } else { thread->sig = _PTHREAD_NO_SIG;#if PTH_TRACE __kdebug_trace(0x9000028, thread, 0, 0, 3, 0);#endif free(thread); } return(0);}
/* ALWAYS called with list lock and return with list lock */int_pthread_find_thread(pthread_t thread){ pthread_t p;
loop: TAILQ_FOREACH(p, &__pthread_head, plist) { if (p == thread) { if (thread->kernel_thread == MACH_PORT_NULL) { UNLOCK(_pthread_list_lock); sched_yield(); LOCK(_pthread_list_lock); goto loop; } return(0); } } return(ESRCH);}
int_pthread_lookup_thread(pthread_t thread, mach_port_t * portp, int only_joinable){ mach_port_t kport; int ret = 0;
if (thread == NULL) return(ESRCH); LOCK(_pthread_list_lock);
if ((ret = _pthread_find_thread(thread)) != 0) { UNLOCK(_pthread_list_lock); return(ret); } if ((only_joinable != 0) && ((thread->detached & PTHREAD_CREATE_DETACHED) != 0)) { UNLOCK(_pthread_list_lock); return(EINVAL); } kport = thread->kernel_thread; UNLOCK(_pthread_list_lock); if (portp != NULL) *portp = kport; return(0);}
/* XXXXXXXXXXXXX Pthread Workqueue Attributes XXXXXXXXXXXXXXXXXX */int pthread_workqueue_attr_init_np(pthread_workqueue_attr_t * attrp){ attrp->queueprio = WORKQ_DEFAULT_PRIOQUEUE; attrp->sig = PTHREAD_WORKQUEUE_ATTR_SIG; attrp->overcommit = 0; return(0);}
int pthread_workqueue_attr_destroy_np(pthread_workqueue_attr_t * attr){ if (attr->sig == PTHREAD_WORKQUEUE_ATTR_SIG) { return (0); } else { return (EINVAL); /* Not an attribute structure! */ }}
int pthread_workqueue_attr_getqueuepriority_np(const pthread_workqueue_attr_t * attr, int * qpriop){ if (attr->sig == PTHREAD_WORKQUEUE_ATTR_SIG) { *qpriop = attr->queueprio; return (0); } else { return (EINVAL); /* Not an attribute structure! */ }}
int pthread_workqueue_attr_setqueuepriority_np(pthread_workqueue_attr_t * attr, int qprio){int error = 0;
if (attr->sig == PTHREAD_WORKQUEUE_ATTR_SIG) { switch(qprio) { case WORKQ_HIGH_PRIOQUEUE: case WORKQ_DEFAULT_PRIOQUEUE: case WORKQ_LOW_PRIOQUEUE: case WORKQ_BG_PRIOQUEUE: attr->queueprio = qprio; break; default: error = EINVAL; } } else { error = EINVAL; } return (error);}
int pthread_workqueue_attr_getovercommit_np(const pthread_workqueue_attr_t * attr, int * ocommp){ if (attr->sig == PTHREAD_WORKQUEUE_ATTR_SIG) { *ocommp = attr->overcommit; return (0); } else { return (EINVAL); /* Not an attribute structure! */ }}
int pthread_workqueue_attr_setovercommit_np(pthread_workqueue_attr_t * attr, int ocomm){int error = 0;
if (attr->sig == PTHREAD_WORKQUEUE_ATTR_SIG) { attr->overcommit = ocomm; } else { error = EINVAL; } return (error);}/* XXXXXXXXXXXXX Pthread Workqueue support routines XXXXXXXXXXXXXXXXXX */
static voidworkqueue_list_lock(){ OSSpinLockLock(&__workqueue_list_lock);}
static voidworkqueue_list_unlock(){ OSSpinLockUnlock(&__workqueue_list_lock);}
intpthread_workqueue_init_np(){ int ret;
if (__workqueue_newspis != 0) return(EPERM); __workqueue_oldspis = 1;
workqueue_list_lock(); ret =_pthread_work_internal_init(); workqueue_list_unlock();
return(ret);}
intpthread_workqueue_requestconcurrency_np(int queue, int request_concurrency){ int error = 0;
if (__workqueue_newspis != 0) return(EPERM);
if (queue < 0 || queue > WORKQ_NUM_PRIOQUEUE) return(EINVAL);
error =__workq_kernreturn(WQOPS_THREAD_SETCONC, NULL, request_concurrency, queue);
if (error == -1) return(errno); return(0);}
voidpthread_workqueue_atfork_prepare(void){ /* * NOTE: Any workq additions here * should be for i386,x86_64 only */ dispatch_atfork_prepare();}
voidpthread_workqueue_atfork_parent(void){ /* * NOTE: Any workq additions here * should be for i386,x86_64 only */ dispatch_atfork_parent();}
voidpthread_workqueue_atfork_child(void){#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) pthread_t self = pthread_self();
__workqueue_list_lock = OS_SPINLOCK_INIT;
/* already using new spis? */ if (__workqueue_newspis != 0) { /* prepare the kernel for workq action */#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) __bsdthread_register(thread_start, start_wqthread, round_page(sizeof(struct _pthread)), _pthread_start, &workq_targetconc[0], (uintptr_t)(&self->tsd[__PTK_LIBDISPATCH_KEY0]) - (uintptr_t)(&self->tsd[0]));#else __bsdthread_register(_pthread_start, _pthread_wqthread, round_page(sizeof(struct _pthread)),NULL,NULL,0);#endif (void)__workq_open(); kernel_workq_setup = 1; return; }
/* not using old spis either? */ if (__workqueue_oldspis == 0) return;
/* * NOTE: workq additions here * are for i386,x86_64 only as * ppc and arm do not support it */ if (kernel_workq_setup != 0){ kernel_workq_setup = 0; _pthread_work_internal_init(); }#endif dispatch_atfork_child();}
static int_pthread_work_internal_init(void){ int i, error; pthread_workqueue_head_t headp; pthread_workqueue_t wq;#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) pthread_t self = pthread_self();#endif
if (kernel_workq_setup == 0) {#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) __bsdthread_register(thread_start, start_wqthread, round_page(sizeof(struct _pthread)), _pthread_start, &workq_targetconc[0], (uintptr_t)(&self->tsd[__PTK_LIBDISPATCH_KEY0]) - (uintptr_t)(&self->tsd[0]));#else __bsdthread_register(_pthread_start, _pthread_wqthread, round_page(sizeof(struct _pthread)),NULL,NULL,0);#endif
_pthread_wq_attr_default.queueprio = WORKQ_DEFAULT_PRIOQUEUE; _pthread_wq_attr_default.sig = PTHREAD_WORKQUEUE_ATTR_SIG;
for( i = 0; i< WORKQ_NUM_PRIOQUEUE; i++) { headp = __pthread_wq_head_tbl[i]; TAILQ_INIT(&headp->wqhead); headp->next_workq = 0; }
__workqueue_pool_ptr = NULL; __workqueue_pool_size = round_page(sizeof(struct _pthread_workitem) * WORKITEM_POOL_SIZE);
__workqueue_pool_ptr = (struct _pthread_workitem *)mmap(NULL, __workqueue_pool_size, PROT_READ|PROT_WRITE, MAP_ANON | MAP_PRIVATE, 0, 0);
if (__workqueue_pool_ptr == MAP_FAILED) { /* Not expected to fail, if it does, always malloc for work items */ __workqueue_nitems = WORKITEM_POOL_SIZE; __workqueue_pool_ptr = NULL; } else __workqueue_nitems = 0;
/* sets up the workitem pool */ grow_workitem();
/* since the size is less than a page, leaving this in malloc pool */ wq = (struct _pthread_workqueue *)malloc(sizeof(struct _pthread_workqueue) * WORKQUEUE_POOL_SIZE); bzero(wq, (sizeof(struct _pthread_workqueue) * WORKQUEUE_POOL_SIZE)); for (i = 0; i < WORKQUEUE_POOL_SIZE; i++) { TAILQ_INSERT_TAIL(&__pthread_workqueue_pool_head, &wq[i], wq_list); }
if (error = __workq_open()) { TAILQ_INIT(&__pthread_workitem_pool_head); TAILQ_INIT(&__pthread_workqueue_pool_head); if (__workqueue_pool_ptr != NULL) { munmap((void *)__workqueue_pool_ptr, __workqueue_pool_size); } free(wq); return(ENOMEM); } kernel_workq_setup = 1; } return(0);}
/* This routine is called with list lock held */static pthread_workitem_talloc_workitem(void){ pthread_workitem_t witem;
if (TAILQ_EMPTY(&__pthread_workitem_pool_head)) { /* the chunk size is set so some multiple of it is pool size */ if (__workqueue_nitems < WORKITEM_POOL_SIZE) { grow_workitem(); } else { workqueue_list_unlock(); witem = malloc(sizeof(struct _pthread_workitem)); workqueue_list_lock(); witem->fromcache = 0; goto out; } } witem = TAILQ_FIRST(&__pthread_workitem_pool_head); TAILQ_REMOVE(&__pthread_workitem_pool_head, witem, item_entry); witem->fromcache = 1;out: witem->flags = 0; witem->item_entry.tqe_next = 0; witem->item_entry.tqe_prev = 0; user_workitem_count++; return(witem);}
/* This routine is called with list lock held */static voidfree_workitem(pthread_workitem_t witem) { user_workitem_count--; witem->flags = 0; if (witem->fromcache != 0) TAILQ_INSERT_TAIL(&__pthread_workitem_pool_head, witem, item_entry); else free(witem);}
static voidgrow_workitem(void){ pthread_workitem_t witemp; int i;
witemp = &__workqueue_pool_ptr[__workqueue_nitems]; bzero(witemp, (sizeof(struct _pthread_workitem) * WORKITEM_CHUNK_SIZE)); for (i = 0; i < WORKITEM_CHUNK_SIZE; i++) { witemp[i].fromcache = 1; TAILQ_INSERT_TAIL(&__pthread_workitem_pool_head, &witemp[i], item_entry); } __workqueue_nitems += WORKITEM_CHUNK_SIZE;}
/* This routine is called with list lock held */static pthread_workqueue_talloc_workqueue(void){ pthread_workqueue_t wq;
if (TAILQ_EMPTY(&__pthread_workqueue_pool_head)) { workqueue_list_unlock(); wq = malloc(sizeof(struct _pthread_workqueue)); workqueue_list_lock(); } else { wq = TAILQ_FIRST(&__pthread_workqueue_pool_head); TAILQ_REMOVE(&__pthread_workqueue_pool_head, wq, wq_list); } user_workq_count++; return(wq);}
/* This routine is called with list lock held */static voidfree_workqueue(pthread_workqueue_t wq) { user_workq_count--; TAILQ_INSERT_TAIL(&__pthread_workqueue_pool_head, wq, wq_list);}
static void_pthread_workq_init(pthread_workqueue_t wq, const pthread_workqueue_attr_t * attr){ bzero(wq, sizeof(struct _pthread_workqueue)); if (attr != NULL) { wq->queueprio = attr->queueprio; wq->overcommit = attr->overcommit; } else { wq->queueprio = WORKQ_DEFAULT_PRIOQUEUE; wq->overcommit = 0; } LOCK_INIT(wq->lock); wq->flags = 0; TAILQ_INIT(&wq->item_listhead); TAILQ_INIT(&wq->item_kernhead);#if WQ_LISTTRACE __kdebug_trace(0x90080ac, wq, &wq->item_listhead, wq->item_listhead.tqh_first, wq->item_listhead.tqh_last, 0);#endif wq->wq_list.tqe_next = 0; wq->wq_list.tqe_prev = 0; wq->sig = PTHREAD_WORKQUEUE_SIG; wq->headp = __pthread_wq_head_tbl[wq->queueprio];}
int valid_workq(pthread_workqueue_t workq){ if (workq->sig == PTHREAD_WORKQUEUE_SIG) return(1); else return(0);}
/* called with list lock */static voidpick_nextworkqueue_droplock(){ int i, curwqprio, val, found; pthread_workqueue_head_t headp; pthread_workqueue_t workq; pthread_workqueue_t nworkq = NULL;
#if WQ_TRACE __kdebug_trace(0x9008098, kernel_workq_count, 0, 0, 0, 0);#endifloop: while (kernel_workq_count < KERNEL_WORKQ_ELEM_MAX) { found = 0; for (i = 0; i < WORKQ_NUM_PRIOQUEUE; i++) { wqreadyprio = i; /* because there is nothing else higher to run */ headp = __pthread_wq_head_tbl[i];
if (TAILQ_EMPTY(&headp->wqhead)) continue; workq = headp->next_workq; if (workq == NULL) workq = TAILQ_FIRST(&headp->wqhead); curwqprio = workq->queueprio; nworkq = workq; /* starting pt */ while (kernel_workq_count < KERNEL_WORKQ_ELEM_MAX) { headp->next_workq = TAILQ_NEXT(workq, wq_list); if (headp->next_workq == NULL) headp->next_workq = TAILQ_FIRST(&headp->wqhead);#if WQ_TRACE __kdebug_trace(0x9008098, kernel_workq_count, workq, 0, 1, 0);#endif val = post_nextworkitem(workq);
if (val != 0) { /* things could have changed so reasses */ /* If kernel queue is full , skip */ if (kernel_workq_count >= KERNEL_WORKQ_ELEM_MAX) break; /* If anything with higher prio arrived, then reevaluate */ if (wqreadyprio < curwqprio) goto loop; /* we need re evaluate again */ /* we can post some more work items */ found = 1; }
/* cannot use workq here as it could be freed */ if (TAILQ_EMPTY(&headp->wqhead)) break; /* if we found nothing to run and only one workqueue in the list, skip */ if ((val == 0) && (workq == headp->next_workq)) break; workq = headp->next_workq; if (workq == NULL) workq = TAILQ_FIRST(&headp->wqhead); if (val != 0) nworkq = workq; /* if we found nothing to run and back to workq where we started */ if ((val == 0) && (workq == nworkq)) break; } if (kernel_workq_count >= KERNEL_WORKQ_ELEM_MAX) break; } /* nothing found to run? */ if (found == 0) break; } workqueue_list_unlock();}
static intpost_nextworkitem(pthread_workqueue_t workq){ int error, prio; pthread_workitem_t witem; pthread_workqueue_head_t headp; void (*func)(pthread_workqueue_t, void *);
if ((workq->flags & PTHREAD_WORKQ_SUSPEND) == PTHREAD_WORKQ_SUSPEND) { return(0); }#if WQ_TRACE __kdebug_trace(0x900809c, workq, workq->item_listhead.tqh_first, 0, 1, 0);#endif if (TAILQ_EMPTY(&workq->item_listhead)) { return(0); } if ((workq->flags & PTHREAD_WORKQ_BARRIER_ON) == PTHREAD_WORKQ_BARRIER_ON) return(0);
witem = TAILQ_FIRST(&workq->item_listhead); headp = workq->headp;#if WQ_TRACE __kdebug_trace(0x900809c, workq, witem, 0, 0xee, 0);#endif if ((witem->flags & PTH_WQITEM_BARRIER) == PTH_WQITEM_BARRIER) {#if WQ_TRACE __kdebug_trace(0x9000064, workq, 0, 0, 2, 0);#endif if ((witem->flags & PTH_WQITEM_APPLIED) != 0) { return(0); } /* Also barrier when nothing is there needs to be handled */ /* Nothing to wait for */ if (workq->kq_count != 0) { witem->flags |= PTH_WQITEM_APPLIED; workq->flags |= PTHREAD_WORKQ_BARRIER_ON; workq->barrier_count = workq->kq_count;#if WQ_TRACE __kdebug_trace(0x9000064, 1, workq->barrier_count, 0, 0, 0);#endif return(1); } else {#if WQ_TRACE __kdebug_trace(0x9000064, 2, workq->barrier_count, 0, 0, 0);#endif if (witem->func != NULL) { /* since we are going to drop list lock */ witem->flags |= PTH_WQITEM_APPLIED; workq->flags |= PTHREAD_WORKQ_BARRIER_ON; workqueue_list_unlock(); func = (void (*)(pthread_workqueue_t, void *))witem->func; (*func)(workq, witem->func_arg);#if WQ_TRACE __kdebug_trace(0x9000064, 3, workq->barrier_count, 0, 0, 0);#endif workqueue_list_lock(); workq->flags &= ~PTHREAD_WORKQ_BARRIER_ON; } TAILQ_REMOVE(&workq->item_listhead, witem, item_entry);#if WQ_LISTTRACE __kdebug_trace(0x90080a8, workq, &workq->item_listhead, workq->item_listhead.tqh_first, workq->item_listhead.tqh_last, 0);#endif free_workitem(witem);#if WQ_TRACE __kdebug_trace(0x9000064, 4, workq->barrier_count, 0, 0, 0);#endif return(1); } } else if ((witem->flags & PTH_WQITEM_DESTROY) == PTH_WQITEM_DESTROY) {#if WQ_TRACE __kdebug_trace(0x9000068, 1, workq->kq_count, 0, 0, 0);#endif if ((witem->flags & PTH_WQITEM_APPLIED) != 0) { return(0); } witem->flags |= PTH_WQITEM_APPLIED; workq->flags |= (PTHREAD_WORKQ_BARRIER_ON | PTHREAD_WORKQ_TERM_ON); workq->barrier_count = workq->kq_count; workq->term_callback = (void (*)(struct _pthread_workqueue *,void *))witem->func; workq->term_callarg = witem->func_arg; TAILQ_REMOVE(&workq->item_listhead, witem, item_entry);#if WQ_LISTTRACE __kdebug_trace(0x90080a8, workq, &workq->item_listhead, workq->item_listhead.tqh_first, workq->item_listhead.tqh_last, 0);#endif if ((TAILQ_EMPTY(&workq->item_listhead)) && (workq->kq_count == 0)) { if (!(TAILQ_EMPTY(&workq->item_kernhead))) {#if WQ_TRACE __kdebug_trace(0x900006c, workq, workq->kq_count, 0, 0xff, 0);#endif } free_workitem(witem); workq->flags |= PTHREAD_WORKQ_DESTROYED;#if WQ_TRACE __kdebug_trace(0x900006c, workq, workq->kq_count, 0, 1, 0);#endif headp = __pthread_wq_head_tbl[workq->queueprio]; if (headp->next_workq == workq) { headp->next_workq = TAILQ_NEXT(workq, wq_list); if (headp->next_workq == NULL) { headp->next_workq = TAILQ_FIRST(&headp->wqhead); if (headp->next_workq == workq) headp->next_workq = NULL; } } workq->sig = 0; TAILQ_REMOVE(&headp->wqhead, workq, wq_list); if (workq->term_callback != NULL) { workqueue_list_unlock(); (*workq->term_callback)(workq, workq->term_callarg); workqueue_list_lock(); } free_workqueue(workq); return(1); } else { TAILQ_INSERT_HEAD(&workq->item_listhead, witem, item_entry);#if WQ_LISTTRACE __kdebug_trace(0x90080b0, workq, &workq->item_listhead, workq->item_listhead.tqh_first, workq->item_listhead.tqh_last, 0);#endif }#if WQ_TRACE __kdebug_trace(0x9000068, 2, workq->barrier_count, 0, 0, 0);#endif return(1); } else {#if WQ_TRACE __kdebug_trace(0x9000060, witem, workq, witem->func_arg, 0xfff, 0);#endif TAILQ_REMOVE(&workq->item_listhead, witem, item_entry);#if WQ_LISTTRACE __kdebug_trace(0x90080a8, workq, &workq->item_listhead, workq->item_listhead.tqh_first, workq->item_listhead.tqh_last, 0);#endif TAILQ_INSERT_TAIL(&workq->item_kernhead, witem, item_entry); if ((witem->flags & PTH_WQITEM_KERN_COUNT) == 0) { workq->kq_count++; witem->flags |= PTH_WQITEM_KERN_COUNT; } OSAtomicIncrement32Barrier(&kernel_workq_count); workqueue_list_unlock(); prio = workq->queueprio; if (workq->overcommit != 0) { prio |= WORKQUEUE_OVERCOMMIT; }
if (( error =__workq_kernreturn(WQOPS_QUEUE_ADD, witem, workq->affinity, prio)) == -1) { OSAtomicDecrement32Barrier(&kernel_workq_count); workqueue_list_lock();#if WQ_TRACE __kdebug_trace(0x900007c, witem, workq, witem->func_arg, workq->kq_count, 0);#endif TAILQ_REMOVE(&workq->item_kernhead, witem, item_entry); TAILQ_INSERT_HEAD(&workq->item_listhead, witem, item_entry);#if WQ_LISTTRACE __kdebug_trace(0x90080b0, workq, &workq->item_listhead, workq->item_listhead.tqh_first, workq->item_listhead.tqh_last, 0);#endif if ((workq->flags & (PTHREAD_WORKQ_BARRIER_ON | PTHREAD_WORKQ_TERM_ON)) != 0) workq->flags |= PTHREAD_WORKQ_REQUEUED; } else workqueue_list_lock();#if WQ_TRACE __kdebug_trace(0x9000060, witem, workq, witem->func_arg, workq->kq_count, 0);#endif return(1); } /* noone should come here */#if 1 printf("error in logic for next workitem\n"); LIBC_ABORT("error in logic for next workitem");#endif return(0);}
void_pthread_wqthread(pthread_t self, mach_port_t kport, void * stackaddr, pthread_workitem_t item, int reuse){ int ret; pthread_attr_t *attrs = &_pthread_attr_default; pthread_workqueue_t workq;#if WQ_DEBUG pthread_t pself;#endif int thread_reuse = 0; int thread_priority = 0; int thread_newspi = 0; int thread_options = 0;
if (reuse & WQ_FLAG_THREAD_NEWSPI) { thread_reuse = reuse & WQ_FLAG_THREAD_REUSE; if ((reuse & WQ_FLAG_THREAD_OVERCOMMIT) != 0) thread_options = WORKQ_ADDTHREADS_OPTION_OVERCOMMIT; thread_priority = reuse & WQ_FLAG_THREAD_PRIOMASK; thread_newspi = 1; workq = NULL; } else { thread_reuse = (reuse == 0)? 0: WQ_FLAG_THREAD_REUSE; workq = item->workq; }
if (thread_reuse == 0) { /* reuse is set to 0, when a thread is newly created to run a workitem */ _pthread_struct_init(self, attrs, stackaddr, DEFAULT_STACK_SIZE, 1, 1); self->wqthread = 1; self->wqkillset = 0; self->parentcheck = 1;
/* These are not joinable threads */ self->detached &= ~PTHREAD_CREATE_JOINABLE; self->detached |= PTHREAD_CREATE_DETACHED;#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) _pthread_set_self(self);#endif#if WQ_TRACE __kdebug_trace(0x9000050, self, item, item->func_arg, 0, 0);#endif self->kernel_thread = kport; if (thread_newspi != 0) { self->fun = (void *(*)(void *))__libdispatch_workerfunction; self->arg = thread_priority; } else { self->fun = (void *(*)(void *))item->func; self->arg = item->func_arg; } /* Add to the pthread list */ LOCK(_pthread_list_lock); TAILQ_INSERT_TAIL(&__pthread_head, self, plist);#if PTH_LISTTRACE __kdebug_trace(0x900000c, self, 0, 0, 10, 0);#endif _pthread_count++; UNLOCK(_pthread_list_lock);
#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) if( (self->thread_id = __thread_selfid()) == (__uint64_t)-1) printf("Failed to set thread_id in pthread_wqthread\n");#endif
} else { /* reuse is set to 1, when a thread is resued to run another work item */#if WQ_TRACE __kdebug_trace(0x9000054, self, item, item->func_arg, 0, 0);#endif /* reset all tsd from 1 to KEYS_MAX */ if (self == NULL) LIBC_ABORT("_pthread_wqthread: pthread %p setup to be NULL", self);
if (thread_newspi != 0) { self->fun = (void *(*)(void *))__libdispatch_workerfunction; self->arg = NULL; } else { self->fun = (void *(*)(void *))item->func; self->arg = item->func_arg; } }
#if WQ_DEBUG if (reuse == 0) { pself = pthread_self(); if (self != pself) {#if WQ_TRACE __kdebug_trace(0x9000078, self, pself, item->func_arg, 0, 0);#endif printf("pthread_self not set: pself %p, passed in %p\n", pself, self); _pthread_set_self(self); pself = pthread_self(); if (self != pself) printf("(2)pthread_self not set: pself %p, passed in %p\n", pself, self); pself = self; } } else { pself = pthread_self(); if (self != pself) { printf("(3)pthread_self not set in reuse: pself %p, passed in %p\n", pself, self); LIBC_ABORT("(3)pthread_self not set in reuse: pself %p, passed in %p", pself, self); } }#endif /* WQ_DEBUG */
if (thread_newspi != 0) { (*__libdispatch_workerfunction)(thread_priority, thread_options, NULL); _pthread_workq_return(self); } else { self->cur_workq = workq; self->cur_workitem = item; OSAtomicDecrement32Barrier(&kernel_workq_count);
ret = (int)(intptr_t)(*self->fun)(self->arg); /* If we reach here without going through the above initialization path then don't go through * with the teardown code path ( e.g. setjmp/longjmp ). Instead just exit this thread. */ if (self != pthread_self()) { pthread_exit(PTHREAD_CANCELED); }
workqueue_exit(self, workq, item); }}
static voidworkqueue_exit(pthread_t self, pthread_workqueue_t workq, pthread_workitem_t item){ pthread_workitem_t baritem; pthread_workqueue_head_t headp; void (*func)(pthread_workqueue_t, void *); workqueue_list_lock();
TAILQ_REMOVE(&workq->item_kernhead, item, item_entry); workq->kq_count--;#if WQ_TRACE __kdebug_trace(0x9000070, self, 1, item->func_arg, workq->kq_count, 0);#endif free_workitem(item);
if ((workq->flags & PTHREAD_WORKQ_BARRIER_ON) == PTHREAD_WORKQ_BARRIER_ON) { workq->barrier_count--;#if WQ_TRACE __kdebug_trace(0x9000084, self, workq->barrier_count, workq->kq_count, 1, 0);#endif if (workq->barrier_count <= 0 ) { /* Need to remove barrier item from the list */ baritem = TAILQ_FIRST(&workq->item_listhead);#if WQ_DEBUG if ((baritem->flags & (PTH_WQITEM_BARRIER | PTH_WQITEM_DESTROY| PTH_WQITEM_APPLIED)) == 0) printf("Incorect bar item being removed in barrier processing\n");#endif /* WQ_DEBUG */ /* if the front item is a barrier and call back is registered, run that */ if (((baritem->flags & PTH_WQITEM_BARRIER) == PTH_WQITEM_BARRIER) && (baritem->func != NULL)) { workqueue_list_unlock(); func = (void (*)(pthread_workqueue_t, void *))baritem->func; (*func)(workq, baritem->func_arg); workqueue_list_lock(); } TAILQ_REMOVE(&workq->item_listhead, baritem, item_entry);#if WQ_LISTTRACE __kdebug_trace(0x90080a8, workq, &workq->item_listhead, workq->item_listhead.tqh_first, workq->item_listhead.tqh_last, 0);#endif free_workitem(baritem); workq->flags &= ~PTHREAD_WORKQ_BARRIER_ON;#if WQ_TRACE __kdebug_trace(0x9000058, self, item, item->func_arg, 0, 0);#endif if ((workq->flags & PTHREAD_WORKQ_TERM_ON) != 0) { headp = __pthread_wq_head_tbl[workq->queueprio]; workq->flags |= PTHREAD_WORKQ_DESTROYED;#if WQ_TRACE __kdebug_trace(0x900006c, workq, workq->kq_count, 0, 2, 0);#endif if (headp->next_workq == workq) { headp->next_workq = TAILQ_NEXT(workq, wq_list); if (headp->next_workq == NULL) { headp->next_workq = TAILQ_FIRST(&headp->wqhead); if (headp->next_workq == workq) headp->next_workq = NULL; } } TAILQ_REMOVE(&headp->wqhead, workq, wq_list); workq->sig = 0; if (workq->term_callback != NULL) { workqueue_list_unlock(); (*workq->term_callback)(workq, workq->term_callarg); workqueue_list_lock(); } free_workqueue(workq); } else { /* if there are higher prio schedulabel item reset to wqreadyprio */ if ((workq->queueprio < wqreadyprio) && (!(TAILQ_EMPTY(&workq->item_listhead)))) wqreadyprio = workq->queueprio; } } } #if WQ_TRACE else { __kdebug_trace(0x9000070, self, 2, item->func_arg, workq->barrier_count, 0); } __kdebug_trace(0x900005c, self, item, 0, 0, 0);#endif pick_nextworkqueue_droplock(); _pthread_workq_return(self);}
static void _pthread_workq_return(pthread_t self){ __workq_kernreturn(WQOPS_THREAD_RETURN, NULL, 0, 0);
/* This is the way to terminate the thread */ _pthread_exit(self, NULL);}
/* XXXXXXXXXXXXX Pthread Workqueue functions XXXXXXXXXXXXXXXXXX */
intpthread_workqueue_setdispatch_np(void (*worker_func)(int, int, void *)){ int error = 0;
if (__workqueue_oldspis != 0) return(EPERM);
__workqueue_newspis = 1;
if (__libdispatch_workerfunction == NULL) { __libdispatch_workerfunction = worker_func; /* check whether the kernel supports new SPIs */ error = __workq_kernreturn(WQOPS_QUEUE_NEWSPISUPP, NULL, 0, 0); if (error == -1){ __libdispatch_workerfunction = NULL; error = ENOTSUP; __workqueue_newspis = 0; } else { /* prepare the kernel for workq action */ (void)__workq_open(); kernel_workq_setup = 1; if (__is_threaded == 0) __is_threaded = 1; __workqueue_newspis = 1; } } else { error = EBUSY; }
return(error);}
intpthread_workqueue_addthreads_np(int queue_priority, int options, int numthreads){ int priority = queue_priority & WQ_FLAG_THREAD_PRIOMASK; int error = 0;
/* new spi not inited yet?? */ if (__workqueue_newspis == 0) return(EPERM);
if ((options & WORKQ_ADDTHREADS_OPTION_OVERCOMMIT) != 0) priority |= WORKQUEUE_OVERCOMMIT; error = __workq_kernreturn(WQOPS_QUEUE_REQTHREADS, NULL, numthreads, priority);
if (error == -1) return(errno); else return(0);}
int pthread_workqueue_create_np(pthread_workqueue_t * workqp, const pthread_workqueue_attr_t * attr){ pthread_workqueue_t wq; pthread_workqueue_head_t headp;
#if defined(__ppc__) IF_ROSETTA() { return(ENOTSUP); }#endif if (__workqueue_newspis != 0) return(EPERM);
if (__workqueue_oldspis == 0) __workqueue_oldspis = 1;
if ((attr != NULL) && (attr->sig != PTHREAD_WORKQUEUE_ATTR_SIG)) { return(EINVAL); }
if (__is_threaded == 0) __is_threaded = 1;
workqueue_list_lock(); if (kernel_workq_setup == 0) { int ret = _pthread_work_internal_init(); if (ret != 0) { workqueue_list_unlock(); return(ret); } } wq = alloc_workqueue(); _pthread_workq_init(wq, attr);
headp = __pthread_wq_head_tbl[wq->queueprio]; TAILQ_INSERT_TAIL(&headp->wqhead, wq, wq_list); if (headp->next_workq == NULL) { headp->next_workq = TAILQ_FIRST(&headp->wqhead); }
workqueue_list_unlock(); *workqp = wq;
return(0);}
int pthread_workqueue_additem_np(pthread_workqueue_t workq, void ( *workitem_func)(void *), void * workitem_arg, pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp){ pthread_workitem_t witem;
if (__workqueue_newspis != 0) return(EPERM);
if (valid_workq(workq) == 0) { return(EINVAL); }
workqueue_list_lock();
/* * Allocate the workitem here as it can drop the lock. * Also we can evaluate the workqueue state only once. */ witem = alloc_workitem(); witem->func = workitem_func; witem->func_arg = workitem_arg; witem->workq = workq;
/* alloc workitem can drop the lock, check the state */ if ((workq->flags & (PTHREAD_WORKQ_IN_TERMINATE | PTHREAD_WORKQ_DESTROYED)) != 0) { free_workitem(witem); workqueue_list_unlock(); *itemhandlep = 0; return(ESRCH); }
if (itemhandlep != NULL) *itemhandlep = (pthread_workitem_handle_t *)witem; if (gencountp != NULL) *gencountp = 0;#if WQ_TRACE __kdebug_trace(0x9008090, witem, witem->func, witem->func_arg, workq, 0);#endif TAILQ_INSERT_TAIL(&workq->item_listhead, witem, item_entry);#if WQ_LISTTRACE __kdebug_trace(0x90080a4, workq, &workq->item_listhead, workq->item_listhead.tqh_first, workq->item_listhead.tqh_last, 0);#endif if (((workq->flags & PTHREAD_WORKQ_BARRIER_ON) == 0) && (workq->queueprio < wqreadyprio)) wqreadyprio = workq->queueprio;
pick_nextworkqueue_droplock();
return(0);}
intpthread_workqueue_getovercommit_np(pthread_workqueue_t workq, unsigned int *ocommp){ if (__workqueue_newspis != 0) return(EPERM);
if (valid_workq(workq) == 0) { return(EINVAL); }
if (ocommp != NULL) *ocommp = workq->overcommit; return(0);}
#else /* !BUILDING_VARIANT ] [ */extern int __unix_conforming;extern int _pthread_count;extern int __workqueue_newspis;extern int __workqueue_oldspis;
extern pthread_lock_t _pthread_list_lock;extern void _pthread_testcancel(pthread_t thread, int isconforming);extern int _pthread_reap_thread(pthread_t th, mach_port_t kernel_thread, void **value_ptr, int conforming);
#endif /* !BUILDING_VARIANT ] */
#if __DARWIN_UNIX03
__private_extern__ void __posix_join_cleanup(void *arg){ pthread_t thread = (pthread_t)arg; int already_exited, res; void * dummy; semaphore_t death; int newstyle;
LOCK(thread->lock); already_exited = (thread->detached & _PTHREAD_EXITED);
newstyle = thread->newstyle; #if WQ_TRACE __kdebug_trace(0x900002c, thread, newstyle, 0, 0, 0);#endif if (newstyle == 0) { death = thread->death; if (!already_exited){ thread->joiner = (struct _pthread *)NULL; UNLOCK(thread->lock); restore_sem_to_pool(death); } else { UNLOCK(thread->lock); while ((res = _pthread_reap_thread(thread, thread->kernel_thread, &dummy, 1)) == EAGAIN) { sched_yield(); } restore_sem_to_pool(death);
}
} else { /* leave another thread to join */ thread->joiner = (struct _pthread *)NULL; UNLOCK(thread->lock); }}
#endif /* __DARWIN_UNIX03 */
/* * Wait for a thread to terminate and obtain its exit value. *//*int pthread_join(pthread_t thread, void **value_ptr)
moved to pthread_cancelable.c */
/* * Cancel a thread */intpthread_cancel(pthread_t thread){#if __DARWIN_UNIX03 if (__unix_conforming == 0) __unix_conforming = 1;#endif /* __DARWIN_UNIX03 */
if (_pthread_lookup_thread(thread, NULL, 0) != 0) return(ESRCH);
/* if the thread is a workqueue thread, then return error */ if (thread->wqthread != 0) { return(ENOTSUP); }#if __DARWIN_UNIX03 int state;
LOCK(thread->lock); state = thread->cancel_state |= _PTHREAD_CANCEL_PENDING; UNLOCK(thread->lock); if (state & PTHREAD_CANCEL_ENABLE) __pthread_markcancel(thread->kernel_thread);#else /* __DARWIN_UNIX03 */ thread->cancel_state |= _PTHREAD_CANCEL_PENDING;#endif /* __DARWIN_UNIX03 */ return (0);}
voidpthread_testcancel(void){ pthread_t self = pthread_self();
#if __DARWIN_UNIX03 if (__unix_conforming == 0) __unix_conforming = 1; _pthread_testcancel(self, 1);#else /* __DARWIN_UNIX03 */ _pthread_testcancel(self, 0);#endif /* __DARWIN_UNIX03 */
}
/* * Query/update the cancelability 'state' of a thread */intpthread_setcancelstate(int state, int *oldstate){#if __DARWIN_UNIX03 if (__unix_conforming == 0) { __unix_conforming = 1; } return (_pthread_setcancelstate_internal(state, oldstate, 1));#else /* __DARWIN_UNIX03 */ return (_pthread_setcancelstate_internal(state, oldstate, 0));#endif /* __DARWIN_UNIX03 */
}
/* * Query/update the cancelability 'type' of a thread */intpthread_setcanceltype(int type, int *oldtype){ pthread_t self = pthread_self(); #if __DARWIN_UNIX03 if (__unix_conforming == 0) __unix_conforming = 1;#endif /* __DARWIN_UNIX03 */
if ((type != PTHREAD_CANCEL_DEFERRED) && (type != PTHREAD_CANCEL_ASYNCHRONOUS)) return EINVAL; self = pthread_self(); LOCK(self->lock); if (oldtype) *oldtype = self->cancel_state & _PTHREAD_CANCEL_TYPE_MASK; self->cancel_state &= ~_PTHREAD_CANCEL_TYPE_MASK; self->cancel_state |= type; UNLOCK(self->lock);#if !__DARWIN_UNIX03 _pthread_testcancel(self, 0); /* See if we need to 'die' now... */#endif /* __DARWIN_UNIX03 */ return (0);}
int pthread_sigmask(int how, const sigset_t * set, sigset_t * oset){#if __DARWIN_UNIX03 int err = 0;
if (__pthread_sigmask(how, set, oset) == -1) { err = errno; } return(err);#else /* __DARWIN_UNIX03 */ return(__pthread_sigmask(how, set, oset));#endif /* __DARWIN_UNIX03 */}
/*intsigwait(const sigset_t * set, int * sig)
moved to pthread_cancelable.c */