跨平台的WatiForSingleObject实现
移植win32程序时,有一个难点就是涉及到内核对象的操作,需要模拟win32的实现。
其中比较奇葩的一个是WaitForSingleObject系列。
Linux中没有类似的timeout实现,模拟这个接口,颇费功夫,做个笔记,以备将来。
头文件
/*
* WIN32 Events for POSIX
* 模拟win32的Event通知等待
*/ #ifndef __LIBROOT_MY_EVENTS_H_
#define __LIBROOT_MY_EVENTS_H_ #if defined(_WIN32) && !defined(CreateEvent)
#error Must include Windows.h prior to including MyEvent.h!
#endif #ifndef WAIT_TIMEOUT
#include <errno.h>
#define WAIT_TIMEOUT ETIMEDOUT
#endif #include <stdint.h> namespace MY_ENVENT
{
#ifdef _WIN32
typedef HANDLE HEVENT;
#else
//Type declarations
struct my_event_t_;
typedef my_event_t_ * HEVENT;
#endif //WIN32-style functions
HEVENT CreateEvent(bool manualReset = false, bool initialState = false,
const CStdString& strEventName = _T(""));
int DestroyEvent(HEVENT event);
int WaitForEvent(HEVENT event, uint64_t milliseconds = -);
int SetEvent(HEVENT event);
int ResetEvent(HEVENT event); int WaitForMultipleEvents(HEVENT *events, int count, bool waitAll, uint64_t milliseconds);
int WaitForMultipleEvents(HEVENT *events, int count, bool waitAll, uint64_t milliseconds, int &index); #ifdef PULSE
int PulseEvent(HEVENT event);
#endif } #endif
使用mutex和condition来模拟
具体实现
/*
* WIN32 Events for Linux
* Linux实现版本
*/
#include "stdafx.h" #ifndef _WIN32 #include "MyEvent.h"
#include <assert.h>
#include <errno.h>
#include <sys/time.h>
#include <pthread.h> #include <algorithm>
#include <deque> namespace MY_ENVENT
{
struct my_mevent_t_
{
pthread_mutex_t Mutex;
pthread_cond_t CVariable;
pthread_condattr_t CVariable_attr; int RefCount;
union
{
int FiredEvent;
int EventsLeft;
} Status;
bool WaitAll;
bool StillWaiting; void Destroy()
{
pthread_mutex_destroy(&Mutex);
pthread_cond_destroy(&CVariable);
pthread_condattr_destroy(&CVariable_attr);
}
};
typedef my_mevent_t_ *HMEVENT; struct my_mevent_info_t_
{
HMEVENT Waiter;
int WaitIndex;
};
typedef my_mevent_info_t_ *HMEVENT_INFO; struct my_event_t_
{
pthread_cond_t CVariable;
pthread_condattr_t CVariable_attr;
pthread_mutex_t Mutex;
bool AutoReset;
bool State;
std::deque<my_mevent_info_t_> RegisteredWaits;
}; bool RemoveExpiredWaitHelper(my_mevent_info_t_ wait)
{
int result = pthread_mutex_trylock(&wait.Waiter->Mutex); if (result == EBUSY)
{
return false;
} assert(result == ); if (wait.Waiter->StillWaiting == false)
{
--wait.Waiter->RefCount;
assert(wait.Waiter->RefCount >= );
if (wait.Waiter->RefCount == )
{
wait.Waiter->Destroy();
delete wait.Waiter;
}
else
{
result = pthread_mutex_unlock(&wait.Waiter->Mutex);
assert(result == );
} return true;
} result = pthread_mutex_unlock(&wait.Waiter->Mutex);
assert(result == ); return false;
} HEVENT CreateEvent(bool manualReset, bool initialState, const CStdString& strEventName)
{
//unused event name
strEventName.c_str(); HEVENT event = new my_event_t_; pthread_condattr_init(&event->CVariable_attr);
#if _POSIX_MONOTONIC_CLOCK > 0
pthread_condattr_setclock(&event->CVariable_attr, CLOCK_MONOTONIC);
#endif
int result = pthread_cond_init(&event->CVariable, &event->CVariable_attr);
assert(result == ); result = pthread_mutex_init(&event->Mutex, );
assert(result == ); event->State = false;
event->AutoReset = !manualReset; if (initialState)
{
result = SetEvent(event);
assert(result == );
} return event;
} int UnlockedWaitForEvent(HEVENT event, uint64_t milliseconds)
{
int result = ;
if (!event->State)
{
//Zero-timeout event state check optimization
if (milliseconds == )
{
return WAIT_TIMEOUT;
} timespec ts;
if (milliseconds != (uint64_t) -)
{
timeval tv;
gettimeofday(&tv, NULL); uint64_t nanoseconds = ((uint64_t) tv.tv_sec) * * * + milliseconds * * + ((uint64_t) tv.tv_usec) * ; ts.tv_sec = nanoseconds / / / ;
ts.tv_nsec = (nanoseconds - ((uint64_t) ts.tv_sec) * * * );
} do
{
//Regardless of whether it's an auto-reset or manual-reset event:
//wait to obtain the event, then lock anyone else out
if (milliseconds != (uint64_t) -)
{
result = pthread_cond_timedwait(&event->CVariable, &event->Mutex, &ts);
}
else
{
result = pthread_cond_wait(&event->CVariable, &event->Mutex);
}
} while (result == && !event->State); if (result == && event->AutoReset)
{
//We've only accquired the event if the wait succeeded
event->State = false;
}
}
else if (event->AutoReset)
{
//It's an auto-reset event that's currently available;
//we need to stop anyone else from using it
result = ;
event->State = false;
}
//Else we're trying to obtain a manual reset event with a signaled state;
//don't do anything return result;
} int WaitForEvent(HEVENT event, uint64_t milliseconds)
{
int tempResult;
if (milliseconds == )
{
tempResult = pthread_mutex_trylock(&event->Mutex);
if (tempResult == EBUSY)
{
return WAIT_TIMEOUT;
}
}
else
{
tempResult = pthread_mutex_lock(&event->Mutex);
} assert(tempResult == ); int result = UnlockedWaitForEvent(event, milliseconds); tempResult = pthread_mutex_unlock(&event->Mutex);
assert(tempResult == ); return result;
} int WaitForMultipleEvents(HEVENT *events, int count, bool waitAll, uint64_t milliseconds)
{
int unused;
return WaitForMultipleEvents(events, count, waitAll, milliseconds, unused);
} int WaitForMultipleEvents(HEVENT *events, int count, bool waitAll, uint64_t milliseconds, int &waitIndex)
{
HMEVENT wfmo = new my_mevent_t_;
pthread_condattr_init(&wfmo->CVariable_attr);
#if _POSIX_MONOTONIC_CLOCK > 0
pthread_condattr_setclock(&wfmo->CVariable_attr, CLOCK_MONOTONIC);
#endif
int result = ;
int tempResult = pthread_mutex_init(&wfmo->Mutex, );
assert(tempResult == ); tempResult = pthread_cond_init(&wfmo->CVariable, &wfmo->CVariable_attr);
assert(tempResult == ); my_mevent_info_t_ waitInfo;
waitInfo.Waiter = wfmo;
waitInfo.WaitIndex = -; wfmo->WaitAll = waitAll;
wfmo->StillWaiting = true;
wfmo->RefCount = ; if (waitAll)
{
wfmo->Status.EventsLeft = count;
}
else
{
wfmo->Status.FiredEvent = -;
} tempResult = pthread_mutex_lock(&wfmo->Mutex);
assert(tempResult == ); bool done = false;
waitIndex = -; for (int i = ; i < count; ++i)
{
waitInfo.WaitIndex = i; //Must not release lock until RegisteredWait is potentially added
tempResult = pthread_mutex_lock(&events[i]->Mutex);
assert(tempResult == ); //Before adding this wait to the list of registered waits, let's clean up old, expired waits while we have the event lock anyway
events[i]->RegisteredWaits.erase(std::remove_if (events[i]->RegisteredWaits.begin(), events[i]->RegisteredWaits.end(), RemoveExpiredWaitHelper), events[i]->RegisteredWaits.end()); if (UnlockedWaitForEvent(events[i], ) == )
{
tempResult = pthread_mutex_unlock(&events[i]->Mutex);
assert(tempResult == ); if (waitAll)
{
--wfmo->Status.EventsLeft;
assert(wfmo->Status.EventsLeft >= );
}
else
{
wfmo->Status.FiredEvent = i;
waitIndex = i;
done = true;
break;
}
}
else
{
events[i]->RegisteredWaits.push_back(waitInfo);
++wfmo->RefCount; tempResult = pthread_mutex_unlock(&events[i]->Mutex);
assert(tempResult == );
}
} timespec ts;
if (!done)
{
if (milliseconds == )
{
result = WAIT_TIMEOUT;
done = true;
}
else if (milliseconds != (uint64_t) -)
{
timeval tv;
gettimeofday(&tv, NULL); uint64_t nanoseconds = ((uint64_t) tv.tv_sec) * * * + milliseconds * * + ((uint64_t) tv.tv_usec) * ; ts.tv_sec = nanoseconds / / / ;
ts.tv_nsec = (nanoseconds - ((uint64_t) ts.tv_sec) * * * );
}
} while (!done)
{
//One (or more) of the events we're monitoring has been triggered? //If we're waiting for all events, assume we're done and check if there's an event that hasn't fired
//But if we're waiting for just one event, assume we're not done until we find a fired event
done = (waitAll && wfmo->Status.EventsLeft == ) || (!waitAll && wfmo->Status.FiredEvent != -); if (!done)
{
if (milliseconds != (uint64_t) -)
{
result = pthread_cond_timedwait(&wfmo->CVariable, &wfmo->Mutex, &ts);
}
else
{
result = pthread_cond_wait(&wfmo->CVariable, &wfmo->Mutex);
} if (result != )
{
break;
}
}
} waitIndex = wfmo->Status.FiredEvent;
wfmo->StillWaiting = false; --wfmo->RefCount;
assert(wfmo->RefCount >= );
if (wfmo->RefCount == )
{
wfmo->Destroy();
delete wfmo;
}
else
{
tempResult = pthread_mutex_unlock(&wfmo->Mutex);
assert(tempResult == );
} return result;
} int DestroyEvent(HEVENT event)
{
int result = ; result = pthread_mutex_lock(&event->Mutex);
assert(result == );
event->RegisteredWaits.erase(std::remove_if (event->RegisteredWaits.begin(), event->RegisteredWaits.end(), RemoveExpiredWaitHelper), event->RegisteredWaits.end());
result = pthread_mutex_unlock(&event->Mutex);
assert(result == ); result = pthread_cond_destroy(&event->CVariable);
pthread_condattr_destroy(&event->CVariable_attr);
assert(result == ); result = pthread_mutex_destroy(&event->Mutex);
assert(result == ); delete event; return ;
} int SetEvent(HEVENT event)
{
int result = pthread_mutex_lock(&event->Mutex);
assert(result == ); event->State = true; //Depending on the event type, we either trigger everyone or only one
if (event->AutoReset)
{
while (!event->RegisteredWaits.empty())
{
HMEVENT_INFO i = &event->RegisteredWaits.front(); result = pthread_mutex_lock(&i->Waiter->Mutex);
assert(result == ); --i->Waiter->RefCount;
assert(i->Waiter->RefCount >= );
if (!i->Waiter->StillWaiting)
{
if (i->Waiter->RefCount == )
{
i->Waiter->Destroy();
delete i->Waiter;
}
else
{
result = pthread_mutex_unlock(&i->Waiter->Mutex);
assert(result == );
}
event->RegisteredWaits.pop_front();
continue;
} event->State = false; if (i->Waiter->WaitAll)
{
--i->Waiter->Status.EventsLeft;
assert(i->Waiter->Status.EventsLeft >= );
//We technically should do i->Waiter->StillWaiting = Waiter->Status.EventsLeft != 0
//but the only time it'll be equal to zero is if we're the last event, so no one
//else will be checking the StillWaiting flag. We're good to go without it.
}
else
{
i->Waiter->Status.FiredEvent = i->WaitIndex;
i->Waiter->StillWaiting = false;
} result = pthread_mutex_unlock(&i->Waiter->Mutex);
assert(result == ); result = pthread_cond_signal(&i->Waiter->CVariable);
assert(result == ); event->RegisteredWaits.pop_front(); result = pthread_mutex_unlock(&event->Mutex);
assert(result == ); return ;
} //event->State can be false if compiled with WFMO support
if (event->State)
{
result = pthread_mutex_unlock(&event->Mutex);
assert(result == ); result = pthread_cond_signal(&event->CVariable);
assert(result == ); return ;
}
}
else
{
for (size_t i = ; i < event->RegisteredWaits.size(); ++i)
{
HMEVENT_INFO info = &event->RegisteredWaits[i]; result = pthread_mutex_lock(&info->Waiter->Mutex);
assert(result == ); --info->Waiter->RefCount;
assert(info->Waiter->RefCount >= ); if (!info->Waiter->StillWaiting)
{
if (info->Waiter->RefCount == )
{
info->Waiter->Destroy();
delete info->Waiter;
}
else
{
result = pthread_mutex_unlock(&info->Waiter->Mutex);
assert(result == );
}
continue;
} if (info->Waiter->WaitAll)
{
--info->Waiter->Status.EventsLeft;
assert(info->Waiter->Status.EventsLeft >= );
//We technically should do i->Waiter->StillWaiting = Waiter->Status.EventsLeft != 0
//but the only time it'll be equal to zero is if we're the last event, so no one
//else will be checking the StillWaiting flag. We're good to go without it.
}
else
{
info->Waiter->Status.FiredEvent = info->WaitIndex;
info->Waiter->StillWaiting = false;
} result = pthread_mutex_unlock(&info->Waiter->Mutex);
assert(result == ); result = pthread_cond_signal(&info->Waiter->CVariable);
assert(result == );
}
event->RegisteredWaits.clear(); result = pthread_mutex_unlock(&event->Mutex);
assert(result == ); result = pthread_cond_broadcast(&event->CVariable);
assert(result == );
} return ;
} int ResetEvent(HEVENT event)
{
int result = pthread_mutex_lock(&event->Mutex);
assert(result == ); event->State = false; result = pthread_mutex_unlock(&event->Mutex);
assert(result == ); return ;
} #ifdef PULSE
int PulseEvent(HEVENT event)
{
//This may look like it's a horribly inefficient kludge with the sole intention of reducing code duplication,
//but in reality this is what any PulseEvent() implementation must look like. The only overhead (function
//calls aside, which your compiler will likely optimize away, anyway), is if only WFMO auto-reset waits are active
//there will be overhead to unnecessarily obtain the event mutex for ResetEvent() after. In all other cases (being
//no pending waits, WFMO manual-reset waits, or any WFSO waits), the event mutex must first be released for the
//waiting thread to resume action prior to locking the mutex again in order to set the event state to unsignaled,
//or else the waiting threads will loop back into a wait (due to checks for spurious CVariable wakeups). int result = SetEvent(event);
assert(result == );
result = ResetEvent(event);
assert(result == ); return ;
}
#endif
} #endif //_WIN32
C++ Code
win32的实现直接套用win api即可,这里就不贴了。
跨平台的WatiForSingleObject实现的更多相关文章
- .NET Core 首例 Office 开源跨平台组件(NPOI Core)
前言 最近项目中,需要使用到 Excel 导出,找了一圈发现没有适用于 .NET Core的,不依赖Office和操作系统限制的 Office 组件,于是萌生了把 NPOI 适配并移植到 .NET C ...
- dotNET跨平台相关文档整理
一直在从事C#开发的相关技术工作,从C# 1.0一路用到现在的C# 6.0, 通常情况下被局限于Windows平台,Mono项目把我们C#程序带到了Windows之外的平台,在工作之余花了很多时间在M ...
- Mono为何能跨平台?聊聊CIL(MSIL)
前言: 其实小匹夫在U3D的开发中一直对U3D的跨平台能力很好奇.到底是什么原理使得U3D可以跨平台呢?后来发现了Mono的作用,并进一步了解到了CIL的存在.所以,作为一个对Unity3D跨平台能力 ...
- TinyWeb v1.0 正式完成第一个Release版本(功能基于 libuv 跨平台库)
使用方法很简单,很容易融入现有项目,使现有项目拥有Web网站功能和WebSocket,以及Socket直连! 并且包含了一个跨平台(windows/linux)工具集合; 嗯,也挺棒的^,^ 在项目中 ...
- 开源一个跨平台运行的服务插件 - TaskCore.MainForm
本次将要很大家分享的是一个跨平台运行的服务插件 - TaskCore.MainForm,此框架是使用.netcore来写的,现在netcore已经支持很多系统平台运行了,所以将以前的Task.Main ...
- Xamarin+Prism开发详解一:PCL跨平台类库与Profile的关系
在[Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用]中提到过以下错误,不知道大伙还记得不: 无法安装程序包"Microsoft.Identity.Client 1.0. ...
- Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用
通过本文你将学会如下内容: 1,如何使用Xamarin开发跨平台(Windows,Android,iOS)应用. 2,如何使用微软的登录界面登入Microsoft账号. 3,如何使用Outlook邮箱 ...
- Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用(后续)
在[Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用]里面提到了Microsoft 身份认证,其实这也是一大块需要注意的地方,特作为后续补充这些知识点.上章是使用了Microsof ...
- H5程序员如何利用cordova开发跨平台应用
什么是Cordova? Cordova以前也叫PhoneGap,它提供了一组设备相关的API,通过这组API,移动应用能够以JavaScript访问原生的设备功能,如摄像头.麦克风等.Cordova还 ...
随机推荐
- Unicode与UTF-8互转(C语言实现)
1. 基础 1.1 ASCII码 我们知道, 在计算机内部, 所有的信息最终都表示为一个二进制的字符串. 每一个二进制 位(bit)有0和1两种状态, 因此八个二进制位就可以组合出 256种状态, 这 ...
- 文件IO 练习题
3.1 当读/写磁盘文件时,本章中描述的函数是否具有缓冲机制?请说明原因. 3.1 所有的磁盘 I/O 都要经过内核的块缓冲区(也称为内核的缓冲区高速缓存),唯一例 外的是对原始磁盘设备的 I/O,但 ...
- WPF仿微软事件和属性窗体,效果更炫!
先看效果图:包含系统颜色.系统字体.支持自定义编辑窗体.集合绑定.提供多类型支持. 这是国外网站上无意中看到的,修改了下 感觉还不错!接下来大概介绍下 经过修修改改只留下了有用的主要部分: 前两项 ...
- android开发之使用拼音搜索汉字
国庆回了趟家,昨天真不想走,离家近的感觉太好.唉,不扯这些,说说今天的正事吧. 上篇博客中介绍了自定义AutoCompleteTextView ,但是用到了一个很蹩脚的技术,就是我们事先把每个汉字的拼 ...
- CGI初识
---恢复内容开始--- 转自http://www.moon-soft.com/program/bbs/readelite887957.htm 用 C/C++ 写 CGI 程序 小传(zhcharle ...
- windows向ubuntu过渡之常用软件安装
好久没有写博客了,介于最近上操作系统实验课,好多同学装上了ubuntu,网上的教程比较杂乱,下面我就总结分享一些安装完ubuntu要安装的常用软件,会持续更新... 1.搜狗拼音安装 (1)在安装输入 ...
- Bootstrap学习——起步
一,前言 个人不是专业从事前端开发,但在一个小公司里工作,作为有过这样经历的程序员都知道,开发一个网站或者是一个管理系统,程序员基本所有的事都包了,真是什么都要懂一点啊.而我个人也不怎么喜欢写CSS和 ...
- .NET设计模式(9):桥接模式(Bridge Pattern)
.NET设计模式(9):桥接模式(Bridge Pattern) 桥接模式(Bridge Pattern) --.NET设计模式系列之九 年月 实现代码如下:..所谓抽象和实现沿着各自维度的变 ...
- Google Code项目代码托管网站上Git版本控制系统使用简明教程
作为一个著名的在线项目代码托管网站,Google Code目前主要支持三种版本控制系统,分别为Git, Mercurial和 Subversion.Subversion即SVN相信大家都已经熟知了,这 ...
- JS常用的7中跨域方式总结
javascript跨域有两种情况: 1.基于同一父域的子域之间,如:a.c.com和b.c.com 2.基于不同的父域之间,如:www.a.com和www.b.com 3.端口的不同,如:ww ...