Add prioritizedworkqueue, thread stuff, ui tweaks, small optimizations in file util

This commit is contained in:
Henrik Rydgard 2013-04-13 21:22:03 +02:00
parent bff1115f0c
commit 25711066df
18 changed files with 587 additions and 40 deletions

View File

@ -41,6 +41,8 @@ LOCAL_SRC_FILES :=\
net/http_client.cpp \
net/resolve.cpp \
profiler/profiler.cpp \
thread/threadutil.cpp \
thread/prioritizedworkqueue.cpp \
gfx_es2/glsl_program.cpp \
gfx_es2/gl_state.cpp \
gfx_es2/draw_buffer.cpp.arm \
@ -62,7 +64,7 @@ LOCAL_SRC_FILES :=\
util/text/utf8.cpp
LOCAL_CFLAGS := -O3 -DGL_GLEXT_PROTOTYPES -DUSING_GLES2 -fsigned-char -fno-strict-aliasing
LOCAL_CPPFLAGS := -fno-exceptions -fno-rtti -std=gnu++0x
LOCAL_CPPFLAGS := -fno-exceptions -std=gnu++11
LOCAL_LDLIBS := -lz
LOCAL_C_INCLUDES := $(LOCAL_PATH)/ext/libzip

View File

@ -1,5 +1,4 @@
#ifndef _BASE_BASICTYPES
#define _BASE_BASICTYPES
#pragma once
#include <stdint.h>
#include <stdlib.h> // for byte swapping
@ -92,5 +91,3 @@ inline uint64 swap64(const uint8* _pData) {return swap64(*(const uint64*)_pData)
#ifndef TCHAR
typedef char TCHAR;
#endif
#endif // _BASE_BASICTYPES

View File

@ -1,19 +1,20 @@
#pragma once
// Simple cross platform mutex implementation.
// Similar to the new C++11 api.
// Windows and pthreads implementations in one.
// TODO: Need to clean up these primitives and put them in a reasonable namespace.
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
// Zap stupid windows defines
// Should move these somewhere clever.
#undef p
#undef MIN
#undef MAX
#undef min
#undef max
#undef DrawText
#undef itoa
@ -125,10 +126,11 @@ public:
void wait(recursive_mutex &mtx) {
// broken
#ifdef _WIN32
//mtx.unlock();
// This has to be horribly racy.
mtx.unlock();
WaitForSingleObject(event_, INFINITE);
ResetEvent(event_); // necessary?
// mtx.lock();
mtx.lock();
#else
pthread_mutex_lock(&mtx.native_handle());
pthread_cond_wait(&event_, &mtx.native_handle());
@ -167,3 +169,77 @@ private:
pthread_cond_t event_;
#endif
};
class condition_variable {
public:
#ifdef _WIN32
#else
#endif
condition_variable() {
#ifdef _WIN32
event_ = CreateEvent(0, FALSE, FALSE, 0);
#else
pthread_cond_init(&event_, NULL);
#endif
}
~condition_variable() {
#ifdef _WIN32
CloseHandle(event_);
#else
pthread_cond_destroy(&event_);
#endif
}
void notify_one() {
#ifdef _WIN32
SetEvent(event_);
#else
pthread_cond_signal(&event_);
#endif
}
// notify_all is not really possible to implement with win32 events?
void wait(recursive_mutex &mtx) {
// broken
#ifdef _WIN32
// This has to be horribly racy.
mtx.unlock();
WaitForSingleObject(event_, INFINITE);
ResetEvent(event_); // necessary?
mtx.lock();
#else
pthread_cond_wait(&event_, &mtx.native_handle());
#endif
}
void wait_for(recursive_mutex &mtx, int milliseconds) {
#ifdef _WIN32
//mtx.unlock();
WaitForSingleObject(event_, milliseconds);
ResetEvent(event_); // necessary?
// mtx.lock();
#else
timespec timeout;
#ifdef __APPLE__
timeval tv;
gettimeofday(&tv, NULL);
timeout.tv_sec = tv.tv_sec;
timeout.tv_nsec = tv.tv_usec * 1000;
#else
clock_gettime(CLOCK_REALTIME, &timeout);
#endif
timeout.tv_sec += milliseconds / 1000;
timeout.tv_nsec += milliseconds * 1000000;
pthread_cond_timedwait(&event_, &mtx.native_handle(), &timeout);
#endif
}
private:
#ifdef _WIN32
HANDLE event_;
#else
pthread_cond_t event_;
#endif
};

View File

@ -168,15 +168,12 @@ bool getFileInfo(const char *path, FileInfo *fileInfo)
#ifdef _WIN32
fileInfo->size = 0;
FILE *f = fopen(path, "rb");
if (f) {
fseek(f, 0, SEEK_END);
fileInfo->size = ftell(f);
fclose(f);
WIN32_FILE_ATTRIBUTE_DATA attrs;
if (GetFileAttributesExA(path, GetFileExInfoStandard, &attrs)) {
fileInfo->size = (uint64_t)attrs.nFileSizeLow | ((uint64_t)attrs.nFileSizeHigh << 32);
}
DWORD attributes = GetFileAttributes(path);
fileInfo->isDirectory = (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
fileInfo->isWritable = (attributes & FILE_ATTRIBUTE_READONLY) == 0;
fileInfo->isDirectory = (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
fileInfo->isWritable = (attrs.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0;
#else
struct stat64 file_info;

View File

@ -1,12 +1,11 @@
#include <vector>
#include "base/basictypes.h"
#include "base/logging.h"
#include "gfx/gl_lost_manager.h"
std::vector<GfxResourceHolder *> *holders;
GfxResourceHolder::~GfxResourceHolder() {}
static bool inLost;
void register_gl_resource_holder(GfxResourceHolder *holder) {

View File

@ -5,7 +5,7 @@
class GfxResourceHolder {
public:
virtual ~GfxResourceHolder();
virtual ~GfxResourceHolder() {}
virtual void GLLost() = 0;
};

View File

@ -33,6 +33,8 @@ float linearOut(int t, int fadeOutLength) {
}
float ease(float val) {
if (val > 1.0f) return 1.0f;
if (val < 0.0f) return 0.0f;
return ((-cosf(val * PI)) + 1.0f) * 0.5;
}

View File

@ -196,7 +196,6 @@
<ClInclude Include="base\scoped_ptr.h" />
<ClInclude Include="base\stats.h" />
<ClInclude Include="base\stringutil.h" />
<ClInclude Include="base\threadutil.h" />
<ClInclude Include="base\timeutil.h" />
<ClInclude Include="data\listable.h" />
<ClInclude Include="ext\cityhash\city.h" />
@ -242,6 +241,9 @@
<ClInclude Include="net\http_client.h" />
<ClInclude Include="net\resolve.h" />
<ClInclude Include="profiler\profiler.h" />
<ClInclude Include="thread\prioritizedworkqueue.h" />
<ClInclude Include="thread\thread.h" />
<ClInclude Include="thread\threadutil.h" />
<ClInclude Include="ui\screen.h" />
<ClInclude Include="ui\ui.h" />
<ClInclude Include="ui\ui_context.h" />
@ -290,7 +292,6 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="base\stringutil.cpp" />
<ClCompile Include="base\threadutil.cpp" />
<ClCompile Include="base\timeutil.cpp" />
<ClCompile Include="ext\cityhash\city.cpp" />
<ClCompile Include="ext\rg_etc1\rg_etc1.cpp" />
@ -331,6 +332,8 @@
<ClCompile Include="net\http_client.cpp" />
<ClCompile Include="net\resolve.cpp" />
<ClCompile Include="profiler\profiler.cpp" />
<ClCompile Include="thread\prioritizedworkqueue.cpp" />
<ClCompile Include="thread\threadutil.cpp" />
<ClCompile Include="ui\screen.cpp" />
<ClCompile Include="ui\ui.cpp" />
<ClCompile Include="ui\ui_context.cpp" />

View File

@ -206,9 +206,6 @@
<ClInclude Include="util\random\rng.h">
<Filter>util</Filter>
</ClInclude>
<ClInclude Include="base\threadutil.h">
<Filter>base</Filter>
</ClInclude>
<ClInclude Include="ext\stb_image\stb_image.h">
<Filter>ext</Filter>
</ClInclude>
@ -239,6 +236,15 @@
<ClInclude Include="ext\rg_etc1\rg_etc1.h">
<Filter>ext</Filter>
</ClInclude>
<ClInclude Include="thread\prioritizedworkqueue.h">
<Filter>thread</Filter>
</ClInclude>
<ClInclude Include="thread\thread.h">
<Filter>thread</Filter>
</ClInclude>
<ClInclude Include="thread\threadutil.h">
<Filter>thread</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="gfx\gl_debug_log.cpp">
@ -388,9 +394,6 @@
<ClCompile Include="util\random\perlin.cpp">
<Filter>util</Filter>
</ClCompile>
<ClCompile Include="base\threadutil.cpp">
<Filter>base</Filter>
</ClCompile>
<ClCompile Include="ext\stb_image\stb_image.c">
<Filter>ext</Filter>
</ClCompile>
@ -423,6 +426,12 @@
<ClCompile Include="ext\rg_etc1\rg_etc1.cpp">
<Filter>ext</Filter>
</ClCompile>
<ClCompile Include="thread\prioritizedworkqueue.cpp">
<Filter>thread</Filter>
</ClCompile>
<ClCompile Include="thread\threadutil.cpp">
<Filter>thread</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="gfx">
@ -482,5 +491,8 @@
<Filter Include="tools">
<UniqueIdentifier>{4515306f-4664-46bf-a89b-abfec5520a15}</UniqueIdentifier>
</Filter>
<Filter Include="thread">
<UniqueIdentifier>{caa41117-1d90-47cc-9fba-f7e670e315a3}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>

View File

@ -0,0 +1,82 @@
#include <functional>
#include "base/logging.h"
#include "thread/thread.h"
#include "thread/prioritizedworkqueue.h"
#if defined(IOS) || (defined(__APPLE__) && !defined(__MAC_10_7))
namespace std {
using tr1::bind;
}
#endif
PrioritizedWorkQueue::~PrioritizedWorkQueue() {
if (!done_) {
ELOG("PrioritizedWorkQueue destroyed but not done!");
}
}
void PrioritizedWorkQueue::Add(PrioritizedWorkQueueItem *item) {
mutex_.lock();
queue_.push_back(item);
mutex_.unlock();
notEmpty_.notify_one();
}
void PrioritizedWorkQueue::Stop() {
mutex_.lock();
done_ = true;
notEmpty_.notify_one();
mutex_.unlock();
}
// The worker should simply call this in a loop. Will block when appropriate.
PrioritizedWorkQueueItem *PrioritizedWorkQueue::Pop() {
mutex_.lock();
while (queue_.empty()) {
notEmpty_.wait(mutex_);
if (done_)
return 0;
}
// Find the top priority item (lowest value).
float best_prio = std::numeric_limits<float>::infinity();
std::vector<PrioritizedWorkQueueItem *>::iterator best = queue_.end();
for (auto iter = queue_.begin(); iter != queue_.end(); ++iter) {
if ((*iter)->priority() < best_prio) {
best = iter;
best_prio = (*iter)->priority();
}
}
PrioritizedWorkQueueItem *poppedItem = *best;
queue_.erase(best);
mutex_.unlock();
return poppedItem;
}
// TODO: This feels ugly. Revisit later.
static std::thread *workThread;
static void threadfunc(PrioritizedWorkQueue *wq) {
while (true) {
PrioritizedWorkQueueItem *item = wq->Pop();
if (!item) {
if (wq->Done())
break;
} else {
item->run();
}
}
}
void ProcessWorkQueueOnThreadWhile(PrioritizedWorkQueue *wq) {
workThread = new std::thread(std::bind(&threadfunc, wq));
}
void StopProcessingWorkQueue(PrioritizedWorkQueue *wq) {
wq->Stop();
workThread->join();
delete workThread;
workThread = 0;
}

View File

@ -0,0 +1,51 @@
#pragma once
#include <vector>
#include <limits>
#include "base/basictypes.h"
#include "base/mutex.h"
#include "thread/threadutil.h"
// Priorities can change dynamically.
// Try to make priority() fast, it will be called a lot.
class PrioritizedWorkQueueItem {
public:
PrioritizedWorkQueueItem() {}
virtual ~PrioritizedWorkQueueItem() {}
virtual void run() = 0;
virtual float priority() = 0; // Low priority value = high priority!
private:
DISALLOW_COPY_AND_ASSIGN(PrioritizedWorkQueueItem);
};
class PrioritizedWorkQueue {
public:
PrioritizedWorkQueue() : done_(false) {}
~PrioritizedWorkQueue();
// Takes ownership.
void Add(PrioritizedWorkQueueItem *item);
// The worker should simply call this in a loop. Will block when appropriate.
PrioritizedWorkQueueItem *Pop();
bool Done() { return done_; }
void Stop();
private:
bool done_;
recursive_mutex mutex_;
condition_variable notEmpty_;
std::vector<PrioritizedWorkQueueItem *> queue_;
DISALLOW_COPY_AND_ASSIGN(PrioritizedWorkQueue);
};
// Starts up a thread that keeps trying to run this workqueue.
// TODO: This feels ugly. Revisit later.
void ProcessWorkQueueOnThreadWhile(PrioritizedWorkQueue *wq);
void StopProcessingWorkQueue(PrioritizedWorkQueue *wq);

310
thread/thread.h Normal file
View File

@ -0,0 +1,310 @@
#ifndef STD_THREAD_H_
#define STD_THREAD_H_
#define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z))
#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ && !defined(ANDROID) && !defined(__SYMBIAN32__)
// GCC 4.4 provides <thread>
#ifndef _GLIBCXX_USE_SCHED_YIELD
#define _GLIBCXX_USE_SCHED_YIELD
#endif
#include <thread>
#else
// partial std::thread implementation for win32/pthread
#include <algorithm>
#if (_MSC_VER >= 1600) || (GCC_VERSION >= GCC_VER(4,3,0) && __GXX_EXPERIMENTAL_CXX0X__)
#define USE_RVALUE_REFERENCES
#endif
//#ifdef __APPLE__
//#import <Foundation/NSAutoreleasePool.h>
//#endif
#if defined(_WIN32)
// WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#if defined(_MSC_VER) && defined(_MT)
// When linking with LIBCMT (the multithreaded C library), Microsoft recommends
// using _beginthreadex instead of CreateThread.
#define USE_BEGINTHREADEX
#include <process.h>
#endif
#ifdef USE_BEGINTHREADEX
#define THREAD_ID unsigned
#define THREAD_RETURN unsigned __stdcall
#else
#define THREAD_ID DWORD
#define THREAD_RETURN DWORD WINAPI
#endif
#define THREAD_HANDLE HANDLE
#else
// PTHREAD
#include <unistd.h>
#ifndef _POSIX_THREADS
#error unsupported platform (no pthreads?)
#endif
#include <pthread.h>
#define THREAD_ID pthread_t
#define THREAD_HANDLE pthread_t
#define THREAD_RETURN void*
#endif
namespace std
{
class thread
{
public:
typedef THREAD_HANDLE native_handle_type;
class id
{
friend class thread;
public:
id() : m_thread(0) {}
id(THREAD_ID _id) : m_thread(_id) {}
bool operator==(const id& rhs) const
{
return m_thread == rhs.m_thread;
}
bool operator!=(const id& rhs) const
{
return !(*this == rhs);
}
bool operator<(const id& rhs) const
{
return m_thread < rhs.m_thread;
}
private:
THREAD_ID m_thread;
};
// no variadic template support in msvc
//template <typename C, typename... A>
//thread(C&& func, A&&... args);
template <typename C>
thread(C func)
{
StartThread(new Func<C>(func));
}
template <typename C, typename A>
thread(C func, A arg)
{
StartThread(new FuncArg<C, A>(func, arg));
}
thread() /*= default;*/ {}
#ifdef USE_RVALUE_REFERENCES
thread(const thread&) /*= delete*/;
thread(thread&& other)
{
#else
thread(const thread& t)
{
// ugly const_cast to get around lack of rvalue references
thread& other = const_cast<thread&>(t);
#endif
swap(other);
}
#ifdef USE_RVALUE_REFERENCES
thread& operator=(const thread&) /*= delete*/;
thread& operator=(thread&& other)
{
#else
thread& operator=(const thread& t)
{
// ugly const_cast to get around lack of rvalue references
thread& other = const_cast<thread&>(t);
#endif
if (joinable())
detach();
swap(other);
return *this;
}
~thread()
{
if (joinable())
detach();
}
bool joinable() const
{
return m_id != id();
}
id get_id() const
{
return m_id;
}
native_handle_type native_handle()
{
#ifdef _WIN32
return m_handle;
#else
return m_id.m_thread;
#endif
}
void join()
{
#ifdef _WIN32
WaitForSingleObject(m_handle, INFINITE);
detach();
#else
pthread_join(m_id.m_thread, NULL);
m_id = id();
#endif
}
void detach()
{
#ifdef _WIN32
CloseHandle(m_handle);
#else
pthread_detach(m_id.m_thread);
#endif
m_id = id();
}
void swap(thread& other)
{
std::swap(m_id, other.m_id);
#ifdef _WIN32
std::swap(m_handle, other.m_handle);
#endif
}
static unsigned hardware_concurrency()
{
#ifdef _WIN32
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
return static_cast<unsigned>(sysinfo.dwNumberOfProcessors);
#else
return 0;
#endif
}
private:
id m_id;
#ifdef _WIN32
native_handle_type m_handle;
#endif
template <typename F>
void StartThread(F* param)
{
#ifdef USE_BEGINTHREADEX
m_handle = (HANDLE)_beginthreadex(NULL, 0, &RunAndDelete<F>, param, 0, &m_id.m_thread);
#elif defined(_WIN32)
m_handle = CreateThread(NULL, 0, &RunAndDelete<F>, param, 0, &m_id.m_thread);
#else
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 1024 * 1024);
if (pthread_create(&m_id.m_thread, &attr, &RunAndDelete<F>, param))
m_id = id();
#endif
}
template <typename C>
class Func
{
public:
Func(C _func) : func(_func) {}
void Run() { func(); }
private:
C const func;
};
template <typename C, typename A>
class FuncArg
{
public:
FuncArg(C _func, A _arg) : func(_func), arg(_arg) {}
void Run() { func(arg); }
private:
C const func;
A arg;
};
template <typename F>
static THREAD_RETURN RunAndDelete(void* param)
{
#ifdef __APPLE__
// NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
#endif
static_cast<F*>(param)->Run();
delete static_cast<F*>(param);
#ifdef __APPLE__
// [pool release];
#endif
return 0;
}
};
namespace this_thread
{
inline void yield()
{
#ifdef _WIN32
SwitchToThread();
#else
sleep(0);
#endif
}
inline thread::id get_id()
{
#ifdef _WIN32
return GetCurrentThreadId();
#else
return pthread_self();
#endif
}
} // namespace this_thread
} // namespace std
#undef USE_RVALUE_REFERENCES
#undef USE_BEGINTHREADEX
#undef THREAD_ID
#undef THREAD_RETURN
#undef THREAD_HANDLE
#endif
#endif

View File

@ -1,7 +1,7 @@
#ifdef _WIN32
#include <windows.h>
#endif
#include "base/threadutil.h"
#include "thread/threadutil.h"
void setCurrentThreadName(const char* szThreadName)
{

View File

@ -3,7 +3,10 @@
#include <functional>
#ifdef _WIN32
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <process.h>
#else
#include <unistd.h>
@ -36,3 +39,18 @@ public:
}
};*/
#ifdef _WIN32
#define THREAD_HANDLE HANDLE
#else
#define THREAD_HANDLE pthread_t
#endif
// TODO: replace this abomination with std::thread
class thread {
public:
virtual void Run();
};

View File

@ -3,9 +3,6 @@
#include "ui/screen.h"
#include "ui/ui.h"
Screen::Screen(bool isUiScreen) : screenManager_(0), isUiScreen_(isUiScreen) { }
Screen::~Screen() { }
ScreenManager::ScreenManager() {
nextScreen_ = 0;
uiContext_ = 0;

View File

@ -33,8 +33,9 @@ class UIContext;
class Screen {
public:
Screen(bool isUiScreen = false);
virtual ~Screen();
Screen(bool isUiScreen = false) : screenManager_(0), isUiScreen_(isUiScreen) { }
virtual ~Screen() {}
virtual void update(InputState &input) = 0;
virtual void render() {}
virtual void deviceLost() {}

View File

@ -234,7 +234,7 @@ int UIImageButton(int id, const LayoutManager &layout, float w, int image, int b
return clicked;
}
int UITextureButton(UIContext *ctx, int id, const LayoutManager &layout, float w, float h, Texture *texture, int button_align) // uses current UI atlas for fetching images.
int UITextureButton(UIContext *ctx, int id, const LayoutManager &layout, float w, float h, Texture *texture, int button_align, uint32_t color) // uses current UI atlas for fetching images.
{
float x, y;
layout.GetPos(&w, &h, &x, &y);
@ -294,7 +294,7 @@ int UITextureButton(UIContext *ctx, int id, const LayoutManager &layout, float w
Texture::Unbind();
}
ui_draw2d.DrawTexRect(x, y, x+w, y+h, 0, 0, 1, 1, 0xFFFFFFFF);
ui_draw2d.DrawTexRect(x, y, x+w, y+h, 0, 0, 1, 1, color);
ui_draw2d.Flush();
ctx->RebindTexture();

View File

@ -20,7 +20,7 @@
#define GEN_ID_LOOP(i) (int)((IMGUI_SRC_ID) + (__LINE__) + (i) * 13612)
#else
#define GEN_ID (__LINE__)
#define GEN_ID_LOOP(i) ((__LINE__) + (i) * 13612)
#define GEN_ID_LOOP(i) ((__LINE__) + ((int)i) * 13612)
#endif
#include "gfx_es2/draw_buffer.h"
@ -244,7 +244,7 @@ void UIReset();
// Returns 1 if clicked
int UIButton(int id, const LayoutManager &layout, float w, float h, const char *text, int button_align);
int UIImageButton(int id, const LayoutManager &layout, float w, int image_id, int button_align); // uses current UI atlas for fetching images.
int UITextureButton(UIContext *ctx, int id, const LayoutManager &layout, float w, float h, Texture *texture, int button_align); // uses current UI atlas for fetching images.
int UITextureButton(UIContext *ctx, int id, const LayoutManager &layout, float w, float h, Texture *texture, int button_align, uint32_t color = 0xFFFFFFFF); // uses current UI atlas for fetching images.
// Returns 1 if clicked, puts the value in *value (where it also gets the current state).
int UICheckBox(int id, int x, int y, const char *text, int align, bool *value);