// This file is part of BOINC. // http://boinc.berkeley.edu // Copyright (C) 2008 University of California // // BOINC is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License // as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // BOINC is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with BOINC. If not, see . // Example multi-thread BOINC application. // It does 64 "units" of computation, where each units is about 1 GFLOP. // It divides this among N "worker" threads. // N is passed in the command line, and defaults to 1. // // The main issue is how to suspend/resume the threads. // The standard BOINC API doesn't work - it assumes that // the initial thread is the only one. // On Linux, there's no API to suspend/resume threads. // All you can do is SIGSTOP/SIGCONT, which affects the whole process. // So we use the following process/thread structure: // // Windows: // Initial thread: // - launches worker threads, // - in polling loop, checks for suspend/resume messages // from the BOINC client, and handles them itself. // Unix: // Initial process // - forks worker process // - in polling loop, checks for worker process completion // - doesn't send status msgs // Worker process // Initial thread: // - forks worker threads, wait for them to finish, exit // - uses BOINC runtime to send status messages (frac done, CPU time) // // Doesn't do checkpointing. #include #include #ifdef _WIN32 #else #include #include #include #include #endif #include "util.h" #include "str_util.h" #include "boinc_api.h" using std::vector; #define DEFAULT_NTHREADS 1 #define TOTAL_UNITS 64 int units_per_thread; #ifdef _WIN32 typedef HANDLE THREAD_ID; typedef UINT (__stdcall *THREAD_FUNC)(void*); #else typedef void* (*THREAD_FUNC)(void*); typedef pthread_t THREAD_ID; #endif #define THREAD_ID_NULL 0 // An abstraction of threads. // A thread function is passed a pointer to its own object, // and sets its ID to THREAD_ID_NULL when it's finished. // struct THREAD { THREAD_ID id; int index; int units_done; THREAD(THREAD_FUNC func, int i) { index = i; units_done = 0; #ifdef _WIN32 id = (HANDLE) _beginthreadex( NULL, 16384, func, this, 0, NULL ); if (!id) { fprintf(stderr, "Can't start thread\n"); exit(1); } #else int retval; retval = pthread_create(&id, 0, func, (void*)this); if (retval) { fprintf(stderr, "can't start thread\n"); exit(1); } #endif } #ifdef _WIN32 void suspend(bool if_susp) { if (if_susp) { SuspendThread(id); } else { ResumeThread(id); } } #endif }; struct THREAD_SET { vector threads; #ifdef _WIN32 void suspend(bool if_susp) { for (unsigned int i=0; iid != THREAD_ID_NULL) t->suspend(if_susp); } fprintf(stderr, "suspended all\n"); } #endif bool all_done() { for (unsigned int i=0; iid != THREAD_ID_NULL) return false; } return true; } int units_done() { int count = 0; for (unsigned int i=0; iunits_done; } return count; } }; // do a billion floating-point ops // (note: I needed to add an arg to this; // otherwise the MS C++ compiler optimizes away // all but the first call to it!) // static double do_a_giga_flop(int foo) { double x = 3.14159*foo; int i; for (i=0; i<500000000; i++) { x += 5.12313123; x *= 0.5398394834; } return x; } #ifdef _WIN32 UINT WINAPI worker(void* p) { #else void* worker(void* p) { #endif THREAD* t = (THREAD*)p; for (int i=0; iunits_done++; fprintf(stderr, "thread %d finished %d: %f\n", t->index, i, x); } t->id = THREAD_ID_NULL; #ifdef _WIN32 return 0; #endif } void main_thread(int nthreads) { int i; #ifdef _WIN32 static BOINC_STATUS status; #endif THREAD_SET thread_set; for (i=0; i