Revive voronoi multi-threaded demo.

BUG=none
TEST=demo for SDK
R=binji@chromium.org, noelallen@chromium.org

Review URL: https://codereview.chromium.org/15732012

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@202991 0039d316-1c4b-4281-b951-d872f2087c98
parent 674de992
...@@ -137,6 +137,16 @@ examples/demo/pi_generator/index.html ...@@ -137,6 +137,16 @@ examples/demo/pi_generator/index.html
examples/demo/pi_generator/Makefile examples/demo/pi_generator/Makefile
examples/demo/pi_generator/manifest.json examples/demo/pi_generator/manifest.json
examples/demo/pi_generator/pi_generator.cc examples/demo/pi_generator/pi_generator.cc
examples/demo/voronoi/Makefile
examples/demo/voronoi/background.js
examples/demo/voronoi/common.js
examples/demo/voronoi/example.js
examples/demo/voronoi/icon128.png
examples/demo/voronoi/index.html
examples/demo/voronoi/manifest.json
examples/demo/voronoi/threadpool.cc
examples/demo/voronoi/threadpool.h
examples/demo/voronoi/voronoi.cc
examples/favicon.ico examples/favicon.ico
examples/getting_started/hello_world/background.js examples/getting_started/hello_world/background.js
examples/getting_started/hello_world/common.js examples/getting_started/hello_world/common.js
......
{
'TOOLS': ['newlib', 'glibc', 'pnacl'],
'TARGETS': [
{
'NAME' : 'voronoi',
'TYPE' : 'main',
'SOURCES' : [
'voronoi.cc',
'threadpool.cc',
'threadpool.h'
],
'LIBS': ['ppapi_cpp', 'ppapi', 'pthread']
}
],
'DATA': [
'example.js',
],
'DEST': 'examples/demo',
'NAME': 'voronoi',
'TITLE': 'Multi-Threaded Voronoi Demo',
'GROUP': 'Demo'
}
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
function moduleDidLoad() {
}
function postThreadFunc(numThreads) {
return function () {
common.naclModule.postMessage('threads: ' + numThreads);
}
}
// Add event listeners after the NaCl module has loaded. These listeners will
// forward messages to the NaCl module via postMessage()
function attachListeners() {
document.getElementById('benchmark').addEventListener('click',
function() {
common.naclModule.postMessage('run benchmark');
common.updateStatus('BENCHMARKING... (please wait)');
});
document.getElementById('drawPoints').addEventListener('click',
function() {
var checked = document.getElementById('drawPoints').checked;
if (checked)
common.naclModule.postMessage('with points');
else
common.naclModule.postMessage('without points');
});
document.getElementById('drawInteriors').addEventListener('click',
function() {
var checked = document.getElementById('drawInteriors').checked;
if (checked)
common.naclModule.postMessage('with interiors');
else
common.naclModule.postMessage('without interiors');
});
var threads = [0, 1, 2, 4, 6, 8, 12, 16, 24, 32];
for (var i = 0; i < threads.length; i++) {
document.getElementById('radio'+i).addEventListener('click',
postThreadFunc(threads[i]));
}
document.getElementById('pointRange').addEventListener('change',
function() {
var value = document.getElementById('pointRange').value;
common.naclModule.postMessage('points: ' + value);
document.getElementById('pointCount').textContent = value + ' points';
});
}
// Handle a message coming from the NaCl module.
// In the Voronoi example, the only message will be the benchmark result.
function handleMessage(message_event) {
var x = (Math.round(message_event.data * 1000) / 1000).toFixed(3);
document.getElementById('result').textContent =
'Result: ' + x + ' seconds';
common.updateStatus('SUCCESS')
}
<!DOCTYPE html>
<html>
<!--
Copyright (c) 2013 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<head>
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="-1">
<title>{{title}}</title>
<script type="text/javascript" src="common.js"></script>
<script type="text/javascript" src="example.js"></script>
</head>
<body {{attrs}} data-width="512" data-height="512">
<h1>{{title}}</h1>
<h2>Status: <code id="statusField">NO-STATUS</code></h2>
<div>
This demo renders the Voronoi diagram for a moving set of points using a
brute force technique.
<br>
Number of points:
<input type="range" id="pointRange"
min="1" max="1024" step="1" value="256" />
<label id="pointCount" >256 points</label>
<br>
Number of threads (0 is main thread):
<input type="radio" name="threadCount" id="radio0" value="0"
checked="checked">
<label for="radio0">0</label>
<input type="radio" name="threadCount" id="radio1" value="1">
<label for="radio1">1</label>
<input type="radio" name="threadCount" id="radio2" value="2">
<label for="radio2">2</label>
<input type="radio" name="threadCount" id="radio3" value="4">
<label for="radio3">4</label>
<input type="radio" name="threadCount" id="radio4" value="6">
<label for="radio4">6</label>
<input type="radio" name="threadCount" id="radio5" value="8">
<label for="radio5">8</label>
<input type="radio" name="threadCount" id="radio6" value="12">
<label for="radio6">12</label>
<input type="radio" name="threadCount" id="radio7" value="16">
<label for="radio7">16</label>
<input type="radio" name="threadCount" id="radio8" value="24">
<label for="radio8">24</label>
<input type="radio" name="threadCount" id="radio9" value="32">
<label for="radio9">32</label>
<br>
<input type="checkbox" id="drawPoints" checked="checked">
<label for="draw_points">Draw Points</label>
<input type="checkbox" id="drawInteriors" checked="checked">
<label for="draw_interiors">Draw Interiors</label>
<input type="submit" id="benchmark" value="Run Benchmark">
<label id="result" name="result"> </label>
</div>
<!-- The NaCl plugin will be embedded inside the element with id "listener".
See common.js.-->
<div id="listener"></div>
</body>
</html>
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "threadpool.h"
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
// TODO(nfullagar): Switch DecCounter to use atomic decrement.
// Initializes mutex, semaphores and a pool of threads. If 0 is passed for
// num_threads, all work will be performed on the dispatch thread.
ThreadPool::ThreadPool(int num_threads)
: threads_(NULL), counter_(0), num_threads_(num_threads), exiting_(false),
user_data_(NULL), user_work_function_(NULL) {
if (num_threads_ > 0) {
int status;
status = pthread_mutex_init(&mutex_, NULL);
if (0 != status) {
fprintf(stderr, "Failed to initialize mutex!\n");
exit(-1);
}
status = sem_init(&work_sem_, 0, 0);
if (-1 == status) {
fprintf(stderr, "Failed to initialize semaphore!\n");
exit(-1);
}
status = sem_init(&done_sem_, 0, 0);
if (-1 == status) {
fprintf(stderr, "Failed to initialize semaphore!\n");
exit(-1);
}
threads_ = new pthread_t[num_threads_];
for (int i = 0; i < num_threads_; i++) {
status = pthread_create(&threads_[i], NULL, WorkerThreadEntry, this);
if (0 != status) {
fprintf(stderr, "Failed to create thread!\n");
exit(-1);
}
}
}
}
// Post exit request, wait for all threads to join, and cleanup.
ThreadPool::~ThreadPool() {
if (num_threads_ > 1) {
PostExitAndJoinAll();
delete[] threads_;
sem_destroy(&done_sem_);
sem_destroy(&work_sem_);
pthread_mutex_destroy(&mutex_);
}
}
// Setup work parameters. This function is called from the dispatch thread,
// when all worker threads are sleeping.
void ThreadPool::Setup(int counter, WorkFunction work, void *data) {
counter_ = counter;
user_work_function_ = work;
user_data_ = data;
}
// Decrement and get the value of the mutex protected counter. This function
// can be called from multiple threads at any given time.
int ThreadPool::DecCounter() {
int v;
pthread_mutex_lock(&mutex_);
{
v = --counter_;
}
pthread_mutex_unlock(&mutex_);
return v;
}
// Set exit flag, post and join all the threads in the pool. This function is
// called only from the dispatch thread, and only when all worker threads are
// sleeping.
void ThreadPool::PostExitAndJoinAll() {
exiting_ = true;
// Wake up all the sleeping worker threads.
for (int i = 0; i < num_threads_; ++i)
sem_post(&work_sem_);
void* retval;
for (int i = 0; i < num_threads_; ++i)
pthread_join(threads_[i], &retval);
}
// Main work loop - one for each worker thread.
void ThreadPool::WorkLoop() {
while (true) {
// Wait for work. If no work is availble, this thread will sleep here.
sem_wait(&work_sem_);
if (exiting_) break;
while (true) {
// Grab a task index to work on from the counter.
int task_index = DecCounter();
if (task_index < 0)
break;
user_work_function_(task_index, user_data_);
}
// Post to dispatch thread work is done.
sem_post(&done_sem_);
}
}
// pthread entry point for a worker thread.
void* ThreadPool::WorkerThreadEntry(void* thiz) {
static_cast<ThreadPool*>(thiz)->WorkLoop();
return NULL;
}
// DispatchMany() will dispatch a set of tasks across worker threads.
// Note: This function will block until all work has completed.
void ThreadPool::DispatchMany(int num_tasks, WorkFunction work, void* data) {
// On entry, all worker threads are sleeping.
Setup(num_tasks, work, data);
// Wake up the worker threads & have them process tasks.
for (int i = 0; i < num_threads_; i++)
sem_post(&work_sem_);
// Worker threads are now awake and busy.
// This dispatch thread will now sleep-wait for the worker threads to finish.
for (int i = 0; i < num_threads_; i++)
sem_wait(&done_sem_);
// On exit, all tasks are done and all worker threads are sleeping again.
}
// DispatchHere will dispatch all tasks on this thread.
void ThreadPool::DispatchHere(int num_tasks, WorkFunction work, void* data) {
for (int i = 0; i < num_tasks; i++)
work(i, data);
}
// Dispatch() will invoke the user supplied work function across
// one or more threads for each task.
// Note: This function will block until all work has completed.
void ThreadPool::Dispatch(int num_tasks, WorkFunction work, void* data) {
if (num_threads_ > 0)
DispatchMany(num_tasks, work, data);
else
DispatchHere(num_tasks, work, data);
}
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Simple thread pool class
#ifndef EXAMPLES_DEMO_VORONOI_THREADPOOL_H_
#define EXAMPLES_DEMO_VORONOI_THREADPOOL_H_
#include <pthread.h>
#include <semaphore.h>
// typdef helper for work function
typedef void (*WorkFunction)(int task_index, void* data);
// ThreadPool is a class to manage num_threads and assign
// them num_tasks of work at a time. Each call
// to Dispatch(..) will block until all tasks complete.
// If 0 is passed in for num_threads, all tasks will be
// issued on the dispatch thread.
class ThreadPool {
public:
void Dispatch(int num_tasks, WorkFunction work, void* data);
explicit ThreadPool(int num_threads);
~ThreadPool();
private:
int DecCounter();
void Setup(int counter, WorkFunction work, void* data);
void DispatchMany(int num_tasks, WorkFunction work, void* data);
void DispatchHere(int num_tasks, WorkFunction work, void* data);
void WorkLoop();
static void* WorkerThreadEntry(void* data);
void PostExitAndJoinAll();
pthread_t* threads_;
int counter_;
const int num_threads_;
bool exiting_;
void* user_data_;
WorkFunction user_work_function_;
pthread_mutex_t mutex_;
sem_t work_sem_;
sem_t done_sem_;
};
#endif // EXAMPLES_DEMO_VORONOI_THREADPOOL_H_
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment