跨平台的WatiForSingleObject实现

移植win32程序时,有一个难点就是涉及到内核对象的操作,需要模拟win32的实现。

其中比较奇葩的一个是WaitForSingleObject系列。

Linux中没有类似的timeout实现,模拟这个接口,颇费功夫,做个笔记,以备将来。

头文件

 1 /*
 2  * WIN32 Events for POSIX
 3  * 模拟win32的Event通知等待
 4 */
 5
 6 #ifndef __LIBROOT_MY_EVENTS_H_
 7 #define __LIBROOT_MY_EVENTS_H_
 8
 9 #if defined(_WIN32) && !defined(CreateEvent)
10 #error Must include Windows.h prior to including MyEvent.h!
11 #endif
12
13 #ifndef WAIT_TIMEOUT
14 #include <errno.h>
15 #define WAIT_TIMEOUT ETIMEDOUT
16 #endif
17
18 #include <stdint.h>
19
20 namespace MY_ENVENT
21 {
22 #ifdef _WIN32
23     typedef HANDLE  HEVENT;
24 #else
25     //Type declarations
26     struct my_event_t_;
27     typedef my_event_t_ * HEVENT;
28 #endif
29
30     //WIN32-style functions
31     HEVENT CreateEvent(bool manualReset = false, bool initialState = false,
32         const CStdString& strEventName = _T(""));
33     int DestroyEvent(HEVENT event);
34     int WaitForEvent(HEVENT event, uint64_t milliseconds = -1);
35     int SetEvent(HEVENT event);
36     int ResetEvent(HEVENT event);
37
38     int WaitForMultipleEvents(HEVENT *events, int count, bool waitAll, uint64_t milliseconds);
39     int WaitForMultipleEvents(HEVENT *events, int count, bool waitAll, uint64_t milliseconds, int &index);
40
41 #ifdef PULSE
42     int PulseEvent(HEVENT event);
43 #endif
44
45 }
46
47 #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 == 0);

        if (wait.Waiter->StillWaiting == false)
        {
            --wait.Waiter->RefCount;
            assert(wait.Waiter->RefCount >= 0);
            if (wait.Waiter->RefCount == 0)
            {
                wait.Waiter->Destroy();
                delete wait.Waiter;
            }
            else
            {
                result = pthread_mutex_unlock(&wait.Waiter->Mutex);
                assert(result == 0);
            }

            return true;
        }

        result = pthread_mutex_unlock(&wait.Waiter->Mutex);
        assert(result == 0);

        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 == 0);

        result = pthread_mutex_init(&event->Mutex, 0);
        assert(result == 0);

        event->State = false;
        event->AutoReset = !manualReset;

        if (initialState)
        {
            result = SetEvent(event);
            assert(result == 0);
        }

        return event;
    }

    int UnlockedWaitForEvent(HEVENT event, uint64_t milliseconds)
    {
        int result = 0;
        if (!event->State)
        {
            //Zero-timeout event state check optimization
            if (milliseconds == 0)
            {
                return WAIT_TIMEOUT;
            }

            timespec ts;
            if (milliseconds != (uint64_t) -1)
            {
                timeval tv;
                gettimeofday(&tv, NULL);

                uint64_t nanoseconds = ((uint64_t) tv.tv_sec) * 1000 * 1000 * 1000 + milliseconds * 1000 * 1000 + ((uint64_t) tv.tv_usec) * 1000;

                ts.tv_sec = nanoseconds / 1000 / 1000 / 1000;
                ts.tv_nsec = (nanoseconds - ((uint64_t) ts.tv_sec) * 1000 * 1000 * 1000);
            }

            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) -1)
                {
                    result = pthread_cond_timedwait(&event->CVariable, &event->Mutex, &ts);
                }
                else
                {
                    result = pthread_cond_wait(&event->CVariable, &event->Mutex);
                }
            } while (result == 0 && !event->State);

            if (result == 0 && 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 = 0;
            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 == 0)
        {
            tempResult = pthread_mutex_trylock(&event->Mutex);
            if (tempResult == EBUSY)
            {
                return WAIT_TIMEOUT;
            }
        }
        else
        {
            tempResult = pthread_mutex_lock(&event->Mutex);
        }

        assert(tempResult == 0);

        int result = UnlockedWaitForEvent(event, milliseconds);

        tempResult = pthread_mutex_unlock(&event->Mutex);
        assert(tempResult == 0);

        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 = 0;
        int tempResult = pthread_mutex_init(&wfmo->Mutex, 0);
        assert(tempResult == 0);

        tempResult = pthread_cond_init(&wfmo->CVariable, &wfmo->CVariable_attr);
        assert(tempResult == 0);

        my_mevent_info_t_ waitInfo;
        waitInfo.Waiter = wfmo;
        waitInfo.WaitIndex = -1;

        wfmo->WaitAll = waitAll;
        wfmo->StillWaiting = true;
        wfmo->RefCount = 1;

        if (waitAll)
        {
            wfmo->Status.EventsLeft = count;
        }
        else
        {
            wfmo->Status.FiredEvent = -1;
        }

        tempResult = pthread_mutex_lock(&wfmo->Mutex);
        assert(tempResult == 0);

        bool done = false;
        waitIndex = -1;

        for (int i = 0; i < count; ++i)
        {
            waitInfo.WaitIndex = i;

            //Must not release lock until RegisteredWait is potentially added
            tempResult = pthread_mutex_lock(&events[i]->Mutex);
            assert(tempResult == 0);

            //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], 0) == 0)
            {
                tempResult = pthread_mutex_unlock(&events[i]->Mutex);
                assert(tempResult == 0);

                if (waitAll)
                {
                    --wfmo->Status.EventsLeft;
                    assert(wfmo->Status.EventsLeft >= 0);
                }
                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 == 0);
            }
        }

        timespec ts;
        if (!done)
        {
            if (milliseconds == 0)
            {
                result = WAIT_TIMEOUT;
                done = true;
            }
            else if (milliseconds != (uint64_t) -1)
            {
                timeval tv;
                gettimeofday(&tv, NULL);

                uint64_t nanoseconds = ((uint64_t) tv.tv_sec) * 1000 * 1000 * 1000 + milliseconds * 1000 * 1000 + ((uint64_t) tv.tv_usec) * 1000;

                ts.tv_sec = nanoseconds / 1000 / 1000 / 1000;
                ts.tv_nsec = (nanoseconds - ((uint64_t) ts.tv_sec) * 1000 * 1000 * 1000);
            }
        }

        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 == 0) || (!waitAll && wfmo->Status.FiredEvent != -1);

            if (!done)
            {
                if (milliseconds != (uint64_t) -1)
                {
                    result = pthread_cond_timedwait(&wfmo->CVariable, &wfmo->Mutex, &ts);
                }
                else
                {
                    result = pthread_cond_wait(&wfmo->CVariable, &wfmo->Mutex);
                }

                if (result != 0)
                {
                    break;
                }
            }
        }

        waitIndex = wfmo->Status.FiredEvent;
        wfmo->StillWaiting = false;

        --wfmo->RefCount;
        assert(wfmo->RefCount >= 0);
        if (wfmo->RefCount == 0)
        {
            wfmo->Destroy();
            delete wfmo;
        }
        else
        {
            tempResult = pthread_mutex_unlock(&wfmo->Mutex);
            assert(tempResult == 0);
        }

        return result;
    }

    int DestroyEvent(HEVENT event)
    {
        int result = 0;

        result = pthread_mutex_lock(&event->Mutex);
        assert(result == 0);
        event->RegisteredWaits.erase(std::remove_if (event->RegisteredWaits.begin(), event->RegisteredWaits.end(), RemoveExpiredWaitHelper), event->RegisteredWaits.end());
        result = pthread_mutex_unlock(&event->Mutex);
        assert(result == 0);

        result = pthread_cond_destroy(&event->CVariable);
        pthread_condattr_destroy(&event->CVariable_attr);
        assert(result == 0);

        result = pthread_mutex_destroy(&event->Mutex);
        assert(result == 0);

        delete event;

        return 0;
    }

    int SetEvent(HEVENT event)
    {
        int result = pthread_mutex_lock(&event->Mutex);
        assert(result == 0);

        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 == 0);

                --i->Waiter->RefCount;
                assert(i->Waiter->RefCount >= 0);
                if (!i->Waiter->StillWaiting)
                {
                    if (i->Waiter->RefCount == 0)
                    {
                        i->Waiter->Destroy();
                        delete i->Waiter;
                    }
                    else
                    {
                        result = pthread_mutex_unlock(&i->Waiter->Mutex);
                        assert(result == 0);
                    }
                    event->RegisteredWaits.pop_front();
                    continue;
                }

                event->State = false;

                if (i->Waiter->WaitAll)
                {
                    --i->Waiter->Status.EventsLeft;
                    assert(i->Waiter->Status.EventsLeft >= 0);
                    //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 == 0);

                result = pthread_cond_signal(&i->Waiter->CVariable);
                assert(result == 0);

                event->RegisteredWaits.pop_front();

                result = pthread_mutex_unlock(&event->Mutex);
                assert(result == 0);

                return 0;
            }

            //event->State can be false if compiled with WFMO support
            if (event->State)
            {
                result = pthread_mutex_unlock(&event->Mutex);
                assert(result == 0);

                result = pthread_cond_signal(&event->CVariable);
                assert(result == 0);

                return 0;
            }
        }
        else
        {
            for (size_t i = 0; i < event->RegisteredWaits.size(); ++i)
            {
                HMEVENT_INFO info = &event->RegisteredWaits[i];

                result = pthread_mutex_lock(&info->Waiter->Mutex);
                assert(result == 0);

                --info->Waiter->RefCount;
                assert(info->Waiter->RefCount >= 0);

                if (!info->Waiter->StillWaiting)
                {
                    if (info->Waiter->RefCount == 0)
                    {
                        info->Waiter->Destroy();
                        delete info->Waiter;
                    }
                    else
                    {
                        result = pthread_mutex_unlock(&info->Waiter->Mutex);
                        assert(result == 0);
                    }
                    continue;
                }

                if (info->Waiter->WaitAll)
                {
                    --info->Waiter->Status.EventsLeft;
                    assert(info->Waiter->Status.EventsLeft >= 0);
                    //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 == 0);

                result = pthread_cond_signal(&info->Waiter->CVariable);
                assert(result == 0);
            }
            event->RegisteredWaits.clear();

            result = pthread_mutex_unlock(&event->Mutex);
            assert(result == 0);

            result = pthread_cond_broadcast(&event->CVariable);
            assert(result == 0);
        }

        return 0;
    }

    int ResetEvent(HEVENT event)
    {
        int result = pthread_mutex_lock(&event->Mutex);
        assert(result == 0);

        event->State = false;

        result = pthread_mutex_unlock(&event->Mutex);
        assert(result == 0);

        return 0;
    }

#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 == 0);
        result = ResetEvent(event);
        assert(result == 0);

        return 0;
    }
#endif
}

#endif //_WIN32

C++ Code

win32的实现直接套用win api即可,这里就不贴了。

时间: 2024-11-05 10:20:11

跨平台的WatiForSingleObject实现的相关文章

react Native如何实现跨平台

react Native是通过虚拟DOM实现跨平台,运行时 将虚拟DOM转换为相应的web编码.android编号.ios编码进行运行的.   代码实现: 01.html: <!DOCTYPE html> <html lang="en"> <head>    <meta charset="UTF-8">    <script src="react.js"></script> 

.NET跨平台之mac 下vs code 多层架构编程

合肥程序员群:49313181.    合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入,申请备注填写姓名+技术+工作年限) Q  Q:408365330     E-Mail:[email protected] 概述: 为了研究跨平台.NET 开发,我打算利用.NET core 编写一个跨平台的cms,这个CMS我也秉着开源的原则放到github上面,为.NET 开源社区做点小小的贡献吧.如果有兴趣的可以联系我一起为.NET开源和跨平台做点小小的贡献吧.EgojitCMS传送

全新的跨平台app软件开发工具——Lae软件开发平台

Lae是一款运行于windows的界面开发工具,具有所见即所得.开发跨平台.UI布局自由.机制简单.维护容易等诸多优点,可以开发同时运行在windows.Linux.MacOX.iOS.Android等系统平台的软件,windows桌面工具软件.管理软件.游戏界面;  linux系统桌面工具软件.管理软件.游戏界面; Mac OSX系统上桌面工具软件.管理软件.游戏界面:安卓系统的APP软件.2D游戏:iOS系统上的APP软件.2D游戏. 感兴趣的朋友请搜索知乎上的Lae软件开发平台介绍,或加入

史上最简单的个人移动APP开发入门--jQuery Mobile版跨平台APP开发

书是人类进步的阶梯. ——高尔基 习大大要求新新人类要有中国梦,鼓励大学生们一毕业就创业.那最好的创业途径是什么呢?就是APP.<构建跨平台APP-jQuery Mobile移动应用实战>就是一本写给没钱没身份没资历的创业小白看的APP书,看完这本书你可以拥有自己的一个APP,不用花钱就能移植到其他移动平台,支持iOS,Android,Windows Phone!!!!!!!!找个最便宜的来练手吧!  小白APP交流Q群:  348632872 清华大学出版社推出的<构建跨平台APP:j

APICloud视频教程_“H5移动跨平台开发”APICloud课程

"H5移动跨平台开发"APICloud课程 课程观看地址:http://www.xuetuwuyou.com/course/169 课程出自学途无忧网:http://www.xuetuwuyou.com 一.课程使用到的软件 APICloud Studuio(或Sublime.WebStorm加上APICloud插件) Google Chrome浏览器 海马玩手机模拟器(或真机) 二.课程目的:     随着IOS.Android等原生APP的开发成本大.维护成本大,"跨平台

跨平台开发,如何进行版本升级?

大家知道,在移动APP开发完毕后,为不短提高用户体验,以及解决一些莫名其妙的bug,总需要对应用进行版本升级.那么问题来了,跨平台开发APP时,怎样快速进行版本升级呢?或许,你需要找对一个同时兼具开发与升级功能的移动开发平台. AppCan开发平台提供的版本升级功能,可以帮助开发者轻松完成应用推送升级.取消升级和应用下架.具体操作如下: 1.推送升级: 进入应用管理界面,点击"推送升级"后进入到升级信息配置页面,升级地址默认为AppCan地址,开发者可以自己搭建升级服务器,将升级地址填

Sublime用户如何快速高效开发跨平台App

2015年9月15日,APICloud举办了一周年开源分享会,发布开源插件支持Sublime用户开发跨平台App,APICloud 开源技术负责人周兴海分享了Sublime关于插件方面相关的内容. Sublime是一款关注度很高的的开发工具,有以下几个特点: 主流前端开发编辑器 体积较小,运行速度快 文本功能强大 支持编译功能且可在控制台看到输出 内嵌python解释器支持插件开发以达到可扩展目的 Package Control:ST支持的大量插件可通过其进行管理 接着,周兴海对APICloud

Sublime插件库新成员基于APICloud快速开发跨平台App

互联网时代强调用户体验,那什么是HTML5跨平台App开发者的编程体验?"不剥夺.不替换开发者喜欢的开发工具,就是人性化的用户体验",APICloud给出了这样的答案! 重磅发布"多开发工具支持策略" "如果,你以为此次分享会APICloud只是讲解Eclipse开源插件代码经验,那就大错特错了!"APICloud CEO刘鑫以调侃的话进行了开场. 经过一年的上线摸索,APICloud团队充分的认识到"剥夺开发者已经习惯的开发工具,替换

Java虚拟机A博娱乐(JVM)以及跨平台原理

A博娱乐相信大家已经了解到Java具有跨平台的特性,可以"一次编译,到处运行",在Windows下编写的程序,无需任何修改就可以在Linux下运行,这是C和C++很难做到的. 那么,跨平台是怎样实现的呢?这就要谈及Java虚拟机(Java Virtual Machine,简称 JVM). JVM也是一个软件,不同的平台有不同的版本.我们编写的Java源码,编译后会生成一种 .class 文件,称为字节码文件.Java虚拟机就是负责将字节码文件翻译成特定平台下的机器码然后运行.也就是说,