Home | History | Annotate | Download | only in utils
      1 /*
      2  * Copyright 2012 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkRunnable.h"
      9 #include "SkThreadPool.h"
     10 #include "SkThreadUtils.h"
     11 #include "SkTypes.h"
     12 
     13 #if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_ANDROID)
     14 #include <unistd.h>
     15 #endif
     16 
     17 // Returns the number of cores on this machine.
     18 static int num_cores() {
     19 #if defined(SK_BUILD_FOR_WIN32)
     20     SYSTEM_INFO sysinfo;
     21     GetSystemInfo(&sysinfo);
     22     return sysinfo.dwNumberOfProcessors;
     23 #elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_ANDROID)
     24     return sysconf(_SC_NPROCESSORS_ONLN);
     25 #else
     26     return 1;
     27 #endif
     28 }
     29 
     30 SkThreadPool::SkThreadPool(int count)
     31 : fDone(false) {
     32     if (count < 0) count = num_cores();
     33     // Create count threads, all running SkThreadPool::Loop.
     34     for (int i = 0; i < count; i++) {
     35         SkThread* thread = SkNEW_ARGS(SkThread, (&SkThreadPool::Loop, this));
     36         *fThreads.append() = thread;
     37         thread->start();
     38     }
     39 }
     40 
     41 SkThreadPool::~SkThreadPool() {
     42     fDone = true;
     43     fReady.lock();
     44     fReady.broadcast();
     45     fReady.unlock();
     46 
     47     // Wait for all threads to stop.
     48     for (int i = 0; i < fThreads.count(); i++) {
     49         fThreads[i]->join();
     50         SkDELETE(fThreads[i]);
     51     }
     52 }
     53 
     54 /*static*/ void SkThreadPool::Loop(void* arg) {
     55     // The SkThreadPool passes itself as arg to each thread as they're created.
     56     SkThreadPool* pool = static_cast<SkThreadPool*>(arg);
     57 
     58     while (true) {
     59         // We have to be holding the lock to read the queue and to call wait.
     60         pool->fReady.lock();
     61         while(pool->fQueue.isEmpty()) {
     62             // Is it time to die?
     63             if (pool->fDone) {
     64                 pool->fReady.unlock();
     65                 return;
     66             }
     67             // wait yields the lock while waiting, but will have it again when awoken.
     68             pool->fReady.wait();
     69         }
     70         // We've got the lock back here, no matter if we ran wait or not.
     71 
     72         // The queue is not empty, so we have something to run.  Claim it.
     73         LinkedRunnable* r = pool->fQueue.tail();
     74 
     75         pool->fQueue.remove(r);
     76 
     77         // Having claimed our SkRunnable, we now give up the lock while we run it.
     78         // Otherwise, we'd only ever do work on one thread at a time, which rather
     79         // defeats the point of this code.
     80         pool->fReady.unlock();
     81 
     82         // OK, now really do the work.
     83         r->fRunnable->run();
     84         SkDELETE(r);
     85     }
     86 
     87     SkASSERT(false); // Unreachable.  The only exit happens when pool->fDone.
     88 }
     89 
     90 void SkThreadPool::add(SkRunnable* r) {
     91     if (NULL == r) {
     92         return;
     93     }
     94 
     95     // If we don't have any threads, obligingly just run the thing now.
     96     if (fThreads.isEmpty()) {
     97         return r->run();
     98     }
     99 
    100     // We have some threads.  Queue it up!
    101     fReady.lock();
    102     LinkedRunnable* linkedRunnable = SkNEW(LinkedRunnable);
    103     linkedRunnable->fRunnable = r;
    104     fQueue.addToHead(linkedRunnable);
    105     fReady.signal();
    106     fReady.unlock();
    107 }
    108