// 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