home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / nsprpub / pr / src / threads / combined / prucpu.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  8.7 KB  |  305 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /*
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  * 
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  * 
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. #include "primpl.h"
  20.  
  21. _PRCPU *_pr_primordialCPU;
  22.  
  23. PRInt32 _pr_md_idle_cpus;       /* number of idle cpus */
  24. /*
  25.  * The idle threads in MxN models increment/decrement _pr_md_idle_cpus.
  26.  * If _PR_HAVE_ATOMIC_OPS is not defined, they can't use the atomic
  27.  * increment/decrement routines (which are based on PR_Lock/PR_Unlock),
  28.  * because PR_Lock asserts that the calling thread is not an idle thread.
  29.  * So we use a _MDLock to protect _pr_md_idle_cpus.
  30.  */
  31. #if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
  32. #ifndef _PR_HAVE_ATOMIC_OPS
  33. static _MDLock _pr_md_idle_cpus_lock;
  34. #endif
  35. #endif
  36. PRUintn _pr_numCPU;
  37. PRInt32 _pr_cpus_exit;
  38. PRInt32 _pr_cpu_affinity_mask = 0;
  39.  
  40. #if !defined (_PR_GLOBAL_THREADS_ONLY)
  41.  
  42. static PRUintn _pr_cpuID;
  43.  
  44. static void PR_CALLBACK _PR_CPU_Idle(void *);
  45. static _PRCPU *_PR_CreateCPU(PRThread *thread, PRBool needQueue);
  46.  
  47. void  _PR_InitCPUs()
  48. {
  49.     PRThread *me = _PR_MD_CURRENT_THREAD();
  50.  
  51.     _pr_cpuID = 0;
  52.     _MD_NEW_LOCK( &_pr_cpuLock);
  53. #if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
  54. #ifndef _PR_HAVE_ATOMIC_OPS
  55.     _MD_NEW_LOCK(&_pr_md_idle_cpus_lock);
  56. #endif
  57. #endif
  58.  
  59. #ifdef HAVE_CUSTOM_USER_THREADS
  60.     _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me);
  61. #endif
  62.  
  63.     /* Now start the first CPU. */
  64.     _pr_primordialCPU = _PR_CreateCPU(me, PR_TRUE);
  65.     _pr_numCPU = 1;
  66.  
  67.     _PR_MD_SET_CURRENT_CPU(_pr_primordialCPU);
  68.  
  69.     /* Initialize cpu for current thread (could be different from me) */
  70.     _PR_MD_CURRENT_THREAD()->cpu = _pr_primordialCPU;
  71.  
  72.     _PR_MD_SET_LAST_THREAD(me);
  73.  
  74.     _PR_MD_INIT_CPUS();
  75. }
  76.  
  77.  
  78. static _PRCPUQueue *_PR_CreateCPUQueue(void)
  79. {
  80.     PRInt32 index;
  81.     _PRCPUQueue *cpuQueue;
  82.     cpuQueue = PR_NEWZAP(_PRCPUQueue);
  83.  
  84.     _MD_NEW_LOCK( &cpuQueue->runQLock );
  85.     _MD_NEW_LOCK( &cpuQueue->sleepQLock );
  86.     _MD_NEW_LOCK( &cpuQueue->miscQLock );
  87.  
  88.     for (index = 0; index < PR_PRIORITY_LAST + 1; index++)
  89.         PR_INIT_CLIST( &(cpuQueue->runQ[index]) );
  90.     PR_INIT_CLIST( &(cpuQueue->sleepQ) );
  91.     PR_INIT_CLIST( &(cpuQueue->pauseQ) );
  92.     PR_INIT_CLIST( &(cpuQueue->suspendQ) );
  93.     PR_INIT_CLIST( &(cpuQueue->waitingToJoinQ) );
  94.  
  95.     cpuQueue->numCPUs = 1;
  96.  
  97.     return cpuQueue;
  98. }
  99.  
  100. /*
  101.  * Create a new CPU.
  102.  */
  103. static _PRCPU *_PR_CreateCPU(PRThread *thread, PRBool needQueue)
  104. {
  105.     _PRCPU *cpu;
  106.  
  107.     /*
  108.     ** Create a new cpu. The assumption this code makes is that the
  109.     ** underlying operating system creates a stack to go with the new
  110.     ** native thread. That stack will be used by the cpu when pausing.
  111.     */
  112.     cpu = PR_NEWZAP(_PRCPU);
  113.     if (cpu) {
  114.  
  115.         cpu->last_clock = PR_IntervalNow();
  116.  
  117.         if (needQueue == PR_TRUE)
  118.             cpu->queue = _PR_CreateCPUQueue();
  119.         else 
  120.             cpu->queue = _PR_MD_CURRENT_CPU()->queue;
  121.    
  122.         if (!cpu->queue) {
  123.             PR_DELETE(cpu);
  124.             return NULL;
  125.         }
  126.  
  127.         /* Before we create any threads on this CPU we have to
  128.          * set the current CPU 
  129.          */
  130.         _PR_MD_SET_CURRENT_CPU(cpu);
  131.         _PR_MD_INIT_RUNNING_CPU(cpu);
  132.         thread->cpu = cpu;
  133.  
  134.         cpu->idle_thread = _PR_CreateThread(PR_SYSTEM_THREAD,
  135.                                            _PR_CPU_Idle,
  136.                                            (void *)cpu,
  137.                                            PR_PRIORITY_NORMAL,
  138.                                            PR_LOCAL_THREAD,
  139.                                            PR_UNJOINABLE_THREAD,
  140.                                            0,
  141.                                                 _PR_IDLE_THREAD);
  142.  
  143.         if (!cpu->idle_thread) {
  144.             /* didn't clean up CPU queue XXXMB */
  145.             PR_DELETE(cpu);
  146.             return NULL;
  147.         } 
  148.         cpu->idle_thread->no_sched = 0;
  149.  
  150.         cpu->thread = thread;
  151.  
  152.         if (_pr_cpu_affinity_mask)
  153.             PR_SetThreadAffinityMask(thread, _pr_cpu_affinity_mask);
  154.  
  155.         /* Created a new CPU */
  156.     _PR_CPU_LIST_LOCK();
  157.         cpu->id = _pr_cpuID++;
  158.         PR_APPEND_LINK(&cpu->links, &_PR_CPUQ());
  159.     _PR_CPU_LIST_UNLOCK();
  160.    }
  161.     return cpu;
  162. }
  163.  
  164. /*
  165. ** This code is used during a cpu's initial creation.
  166. */
  167. static void _PR_RunCPU(void *unused)
  168. {
  169. #if defined(XP_MAC)
  170. #pragma unused (unused)
  171. #endif
  172.  
  173.     _PRCPU *cpu;
  174.     PRThread *me = _PR_MD_CURRENT_THREAD();
  175.  
  176.     PR_ASSERT(NULL != me);
  177.  
  178. #ifdef HAVE_CUSTOM_USER_THREADS
  179.     _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me);
  180. #endif
  181.  
  182.     me->no_sched = 1;
  183.     cpu = _PR_CreateCPU(me, PR_TRUE);
  184.  
  185.     _PR_MD_SET_CURRENT_CPU(cpu);
  186.     _PR_MD_SET_CURRENT_THREAD(cpu->thread);
  187.     me->cpu = cpu;
  188.     while(1) {
  189.         PRInt32 is;
  190.         if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is);
  191.         _PR_MD_START_INTERRUPTS();
  192.         _PR_MD_SWITCH_CONTEXT(me);
  193.     }
  194. }
  195.  
  196. static void PR_CALLBACK _PR_CPU_Idle(void *_cpu)
  197. {
  198.     _PRCPU *cpu = (_PRCPU *)_cpu;
  199.     PRThread *me = _PR_MD_CURRENT_THREAD();
  200.  
  201.     PR_ASSERT(NULL != me);
  202.  
  203.     me->cpu = cpu;
  204.     cpu->idle_thread = me;
  205.     if (_MD_LAST_THREAD())
  206.         _MD_LAST_THREAD()->no_sched = 0;
  207.     if (!_PR_IS_NATIVE_THREAD(me)) _PR_SET_INTSOFF(0);
  208.     while(1) {
  209.         PRInt32 is;
  210.         PRIntervalTime timeout;
  211.  
  212.         if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is);
  213.  
  214.         _PR_RUNQ_LOCK(cpu);
  215. #if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
  216. #ifdef _PR_HAVE_ATOMIC_OPS
  217.         _PR_MD_ATOMIC_INCREMENT(&_pr_md_idle_cpus);
  218. #else
  219.         _PR_MD_LOCK(&_pr_md_idle_cpus_lock);
  220.         _pr_md_idle_cpus++;
  221.         _PR_MD_UNLOCK(&_pr_md_idle_cpus_lock);
  222. #endif /* _PR_HAVE_ATOMIC_OPS */
  223. #endif
  224.         /* If someone on runq; do a nonblocking PAUSECPU */
  225.         if (_PR_RUNQREADYMASK(me->cpu) != 0) {
  226.             _PR_RUNQ_UNLOCK(cpu);
  227.             timeout = PR_INTERVAL_NO_WAIT;
  228.         } else {
  229.             _PR_RUNQ_UNLOCK(cpu);
  230.  
  231.             _PR_SLEEPQ_LOCK(cpu);
  232.             if (PR_CLIST_IS_EMPTY(&_PR_SLEEPQ(me->cpu))) {
  233.                 timeout = PR_INTERVAL_NO_TIMEOUT;
  234.             } else {
  235.                 PRThread *wakeThread;
  236.                 wakeThread = _PR_THREAD_PTR(_PR_SLEEPQ(me->cpu).next);
  237.                 timeout = wakeThread->sleep;
  238.             }
  239.             _PR_SLEEPQ_UNLOCK(cpu);
  240.         }
  241.  
  242.  
  243.         /* Wait for an IO to complete */
  244.         (void)_PR_MD_PAUSE_CPU(timeout);
  245.  
  246. #if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY)
  247. #ifdef _PR_HAVE_ATOMIC_OPS
  248.         _PR_MD_ATOMIC_DECREMENT(&_pr_md_idle_cpus);
  249. #else
  250.         _PR_MD_LOCK(&_pr_md_idle_cpus_lock);
  251.         _pr_md_idle_cpus--;
  252.         _PR_MD_UNLOCK(&_pr_md_idle_cpus_lock);
  253. #endif /* _PR_HAVE_ATOMIC_OPS */
  254. #endif
  255.  
  256.         _PR_ClockInterrupt();
  257.  
  258.         /* Now schedule any thread that is on the runq
  259.          * INTS must be OFF when calling PR_Schedule()
  260.          */
  261.         me->state = _PR_RUNNABLE;
  262.         _PR_MD_SWITCH_CONTEXT(me);
  263.         if (!_PR_IS_NATIVE_THREAD(me)) _PR_FAST_INTSON(is);
  264.     }
  265. }
  266. #endif /* _PR_GLOBAL_THREADS_ONLY */
  267.  
  268.  
  269. PR_IMPLEMENT(void) PR_SetConcurrency(PRUintn numCPUs)
  270. {
  271. #if !defined(_PR_GLOBAL_THREADS_ONLY) && !defined(_PR_LOCAL_THREADS_ONLY)
  272.  
  273.     PRUintn newCPU;
  274.     PRThread *cpu;
  275.  
  276.     if (!_pr_initialized) _PR_ImplicitInitialization();
  277.     
  278.     _PR_CPU_LIST_LOCK();
  279.     if (_pr_numCPU < numCPUs) {
  280.         newCPU = numCPUs - _pr_numCPU;
  281.         _pr_numCPU = numCPUs;
  282.     } else newCPU = 0;
  283.     _PR_CPU_LIST_UNLOCK();
  284.  
  285.     for (; newCPU; newCPU--) {
  286.         cpu = _PR_CreateThread(PR_SYSTEM_THREAD,
  287.                               _PR_RunCPU,
  288.                               NULL,
  289.                               PR_PRIORITY_NORMAL,
  290.                               PR_GLOBAL_THREAD,
  291.                               PR_UNJOINABLE_THREAD,
  292.                               0,
  293.                               _PR_IDLE_THREAD);
  294.     }
  295. #endif
  296. }
  297.  
  298. PR_IMPLEMENT(_PRCPU *) _PR_GetPrimordialCPU(void)
  299. {
  300.     if (_pr_primordialCPU)
  301.         return _pr_primordialCPU;
  302.     else
  303.         return _PR_MD_CURRENT_CPU();
  304. }
  305.