移植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实现的更多相关文章

  1. .NET Core 首例 Office 开源跨平台组件(NPOI Core)

    前言 最近项目中,需要使用到 Excel 导出,找了一圈发现没有适用于 .NET Core的,不依赖Office和操作系统限制的 Office 组件,于是萌生了把 NPOI 适配并移植到 .NET C ...

  2. dotNET跨平台相关文档整理

    一直在从事C#开发的相关技术工作,从C# 1.0一路用到现在的C# 6.0, 通常情况下被局限于Windows平台,Mono项目把我们C#程序带到了Windows之外的平台,在工作之余花了很多时间在M ...

  3. Mono为何能跨平台?聊聊CIL(MSIL)

    前言: 其实小匹夫在U3D的开发中一直对U3D的跨平台能力很好奇.到底是什么原理使得U3D可以跨平台呢?后来发现了Mono的作用,并进一步了解到了CIL的存在.所以,作为一个对Unity3D跨平台能力 ...

  4. TinyWeb v1.0 正式完成第一个Release版本(功能基于 libuv 跨平台库)

    使用方法很简单,很容易融入现有项目,使现有项目拥有Web网站功能和WebSocket,以及Socket直连! 并且包含了一个跨平台(windows/linux)工具集合; 嗯,也挺棒的^,^ 在项目中 ...

  5. 开源一个跨平台运行的服务插件 - TaskCore.MainForm

    本次将要很大家分享的是一个跨平台运行的服务插件 - TaskCore.MainForm,此框架是使用.netcore来写的,现在netcore已经支持很多系统平台运行了,所以将以前的Task.Main ...

  6. Xamarin+Prism开发详解一:PCL跨平台类库与Profile的关系

    在[Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用]中提到过以下错误,不知道大伙还记得不: 无法安装程序包"Microsoft.Identity.Client 1.0. ...

  7. Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用

    通过本文你将学会如下内容: 1,如何使用Xamarin开发跨平台(Windows,Android,iOS)应用. 2,如何使用微软的登录界面登入Microsoft账号. 3,如何使用Outlook邮箱 ...

  8. Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用(后续)

    在[Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用]里面提到了Microsoft 身份认证,其实这也是一大块需要注意的地方,特作为后续补充这些知识点.上章是使用了Microsof ...

  9. H5程序员如何利用cordova开发跨平台应用

    什么是Cordova? Cordova以前也叫PhoneGap,它提供了一组设备相关的API,通过这组API,移动应用能够以JavaScript访问原生的设备功能,如摄像头.麦克风等.Cordova还 ...

随机推荐

  1. 使用val()方法设置表单中的默认选中项

    有时候我们展示给用户的表单中的checkbox,radio,selec等标签的一些项是默认选中的.比方:当用户改动文章的时候,假设相应的栏目为下拉框的话,那么它的默认选中值应该是原来的栏目位置. 能够 ...

  2. C# 动态创建出来的窗体间的通讯 delegate1

    附件 http://files.cnblogs.com/xe2011/CSharp_WindowsForms_delegate01.rar 需要每个窗体是独立存在,禁止相与引用窗体 这样干净并且可以反 ...

  3. Unity3d + NGUI 多分辨率适应

    更多型号适合的移动终端 现在我们要介绍的<链战争>游戏改编方法,这种适应方法UI这是一个基本维度,背景是一个基本的尺寸.背景比UI没有实际影响某一部分的额外部分,这样就避免了适应iPhon ...

  4. APUE 读书笔记 -----孤儿进程与僵尸进程[总结] +数据结构+C

    http://www.cnblogs.com/Anker/p/3271773.html

  5. Tcp抓包以及tcp状态解释

    tcp三次握手 发送端发送一个SYN=1,ACK=0标志的数据包给接收端,请求进行连接,这是第一次握手:接收端收到请求并且允许连接的话,就会发送一个SYN=1,ACK=1标志的数据包给发送端,告诉它, ...

  6. day-8

    /* 倒数8天了 今天高考报名 两个班主任都来了23333 然后填班主任姓名就尴尬了23333 上午考试 挺不错的题目 都不是很水 但需要思考 而我是一脸的懵逼~~ 下午整理各种板子 主要是图论的 最 ...

  7. LA 4329(树状数组)

    题目描述: N <tex2html_verbatim_mark>(3N20000) <tex2html_verbatim_mark>ping pong players live ...

  8. 锱铢必较,从(function(){}())与(function(){})()说起

    今天做JsHint时,碰到一个警告:应该使用(function(){}())而不是(function(){})();看到这个我心想,这两种函数自执行有什么区别吗?自执行用了这么久,感觉对其理解仍然有点 ...

  9. couchbase failover 集群故障自动转移方案研究!

    最近迷上Couchbase了,现在所有的站点全部试用Couchbase进行缓存及持久化,这样以来貌似风险比较大啊,缓存服务器挂了就完了. 看到有讲到Couchbase的集群方案很简单,于是照着教程做了 ...

  10. WisDom.Net 框架设计(五) 权限设计

    WisDom.Net --权限设计 1.需求分析     基本在所有的管理系统中都离不开权限管理.可以这么说,权限管理是管理系统的核心所在. 权限管理说白一些就是每个人能够做什么,不能够做什么.可以说 ...