跨平台的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还 ...
随机推荐
- SQL查询记录添加序号(HANA)
语法:ROW_NUMBER() OVER(PARTITION BY COLUMN ORDER BY COLUMN) row_number() OVER() 从1开始,为每一条分组记录返回一个数字,这里 ...
- KVM地址翻译流程及EPT页表的建立过程
本博文为原创,遵循CC3.0协议,转载请注明出处:http://blog.csdn.net/lux_veritas/article/details/9284635 ------------------ ...
- Android.mk 的含义
LOCAL_PATH:=$(call my-dir) LOCAL_PATH是定义源文件在哪个目录用的. my-dir 是个定义的宏方法, $(call my-dir)就是调用这个叫 my-dir的宏方 ...
- PowerShell 导出SharePoint管理中心解决方式
PowerShell 导出SharePoint管理中心解决方式 SharePoint QQ群有人问能不能下载(导出)管理中心里的解决方式.由于在管理中心中点击解决方式会进入还有一个页面 ...
- (转载)github简单使用教程
github是一个基于git的代码托管平台,付费用户可以建私人仓库,我们一般的免费用户只能使用公共仓库,也就是代码要公开.对于一般人来说公共仓库就已经足够了,而且我们也没多少代码来管理,O(∩_∩)O ...
- HDU2085JAVA
核反应堆 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
- 异步DNS解析的实现
在高性能爬虫为什么使用定制DNS客户端一文中阐述了DNS解析是网络爬虫的瓶颈. 目前主要有两种方法来提高DNS解析效率: 1. 基于多线程的DNS 解析 2. 基于NIO的DNS解析 dnsjava中 ...
- Android(java)学习笔记176:BroadcastReceiver之 短信发送的广播接收者
有时候,我们需要开发出来一个短信监听器,监听用户发送的短信记录,下面就是一个案例,这里同样需要使用广播机制. 下面同样是代码示例,MainActivity.java 和 activity_main. ...
- docker build lnmp(未完成。。。)
docker pull centos # 拉取镜像到本地 docker run -i -t -p 8000:80 --name=centosDev centos cat /etc/redhat-rel ...
- CentOS 6.7安装配置Ansible
1.准备CentOS环境 yum update && yum upgrade 2.控制服务器与被管理服务器要求 Master:Python 2.6+ Slave:Python 2.4+ ...