VincentWei / MiniGUI

A modern and mature cross-platform window system for embedded systems and smart IoT devices.
http://www.minigui.com
GNU General Public License v3.0
676 stars 157 forks source link

关于定时器被异常触发的问题 #116

Open htk719809837 opened 1 year ago

htk719809837 commented 1 year ago

你好,目前5.0出现了timer被异常触发的问题,这个问题比较着急,希望尽快修复 1、minigui运行于多线程模式; 2、A界面为CreateMainWindow或者CreateMainWindowIndirectParam创建的界面。在A界面点击按钮,创建一个B界面,B界面为DialogBoxIndirectParam创捷的对话框; 3、在B对话框点击按钮,按钮触发关闭消息给B对话框,B对话框COLSE消息内,先调用SendMsg接口发送一个自定义消息MY_MSG给A界面;然后在调用EndDialog关闭自己; 4、A界面的MY_MSG中,先处理大量业务(可以使用超大for循环模拟,大概需要卡住3秒以上),然后设置一个两秒定时器; 5、在业务被处理完后,原本2秒后触发的定时器在15毫秒左右,就被异常触发了;

VincentWei commented 1 year ago

有关这个问题,有几个疑问:

  1. 是确定可以复现的,还是只是偶现?
  2. 能否提供简化的复现代码(大量业务代码可以简单替换为 sleep() 函数的调用)?
htk719809837 commented 1 year ago

在ubuntu20上运行,是一个必现问题

include

include <minigui/common.h>

include <minigui/minigui.h>

include <minigui/gdi.h>

include <minigui/window.h>

include <minigui/control.h>

include

include <sys/time.h>

define MSG_TIMER_START (0x900)

static void mytime() { struct timeval tTimeVal; gettimeofday(&tTimeVal, NULL); struct tm *tTM = localtime(&tTimeVal.tv_sec); printf("%04d-%02d-%02d %02d:%02d:%02d.%03ld.%03ld\n\n", tTM->tm_year + 1900, tTM->tm_mon + 1, tTM->tm_mday, tTM->tm_hour, tTM->tm_min, tTM->tm_sec,
tTimeVal.tv_usec / 1000, tTimeVal.tv_usec % 1000); }

void input_back_button_deal(HWND hWnd, int dwMessage, WPARAM dwParam, LPARAM lParam) { if (BN_CLICKED != dwParam) { return; }

SendMessage(GetParent(hWnd),MSG_CLOSE,0,0);
return;

}

static int gui_input_window_proc(HWND hWnd, int message, WPARAM wParam, LPARAM lParam) { HWND hWnd2; switch (message) { case MSG_CREATE:

            hWnd2 = CreateWindowEx(CTRL_BUTTON,
    "2",
    WS_VISIBLE,
    0,
    100,
    0, 
    0,
    100,
    50,
    hWnd,0);
           SetNotificationCallback(hWnd2, (NOTIFPROC)input_back_button_deal);
        break;
    case MSG_CLOSE:
        SendMessage(GetHosting(hWnd),MSG_TIMER_START,0,0);
        EndDialog(hWnd,0);
        break;
    default:
        return DefaultDialogProc (hWnd, message, wParam, lParam);
        break;
}

return 0;

}

HWND input_wnd_start(HWND hwnd) { DLGTEMPLATE stDlg = {0}; stDlg.dwStyle = WS_VISIBLE; stDlg.dwExStyle = WS_EX_NOCLOSEBOX | WS_EX_AUTOSECONDARYDC | WS_EX_TROUNDCNS | WS_EX_BROUNDCNS; stDlg.x = 100; stDlg.y = 100; stDlg.w = 200; stDlg.h = 200; stDlg.caption = "22222"; DialogBoxIndirectParam(&stDlg, hwnd, gui_input_window_proc, 0); }

void input_button_deal(HWND hWnd, int dwMessage, WPARAM dwParam, LPARAM lParam) { if (BN_CLICKED != dwParam) { return; } input_wnd_start(GetParent(hWnd)); return ; }

static LRESULT HelloWinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { HWND hwnd1; switch (message) { case MSG_CREATE: hwnd1 = CreateWindowEx(CTRL_BUTTON, "1", WS_VISIBLE, 0, 0, 0, 0, 100, 50, hWnd,0); SetNotificationCallback(hwnd1, (NOTIFPROC)input_button_deal); hwnd1 = CreateWindowEx(CTRL_STATIC, "2222", WS_VISIBLE, 10, 0, 0, 60, 100, 50, hWnd,0); ShowWindow(hwnd1,SW_HIDE); break; case MSG_TIMER_START:

mytime();
printf("rec msg!!!\n\n\n");
    for (int i = 0; i <= 2000000000;i++)
    {}
     //sleep(500);
     printf("out if deal!!!\n\n\n");
mytime();
 printf("set time!!!\n\n\n");
if (IsTimerInstalled(hWnd, 11))
    {
    KillTimer(hWnd, 11);
    }
SetTimer(hWnd, 11, 200);
break;
case MSG_TIMER:

mytime();
 printf("timer!!!\n\n\n");
     ShowWindow(hwnd1,SW_SHOW);
    case MSG_DESTROY:
        DestroyAllControls (hWnd);
        break;

    case MSG_CLOSE:
        DestroyMainWindow (hWnd);
        PostQuitMessage (hWnd);
        break;
}

return DefaultMainWinProc(hWnd, message, wParam, lParam);

}

int MiniGUIMain (int argc, const char* argv[]) { MSG Msg; HWND hMainWnd; MAINWINCREATE CreateInfo;

ifdef _MGRM_PROCESSES

JoinLayer(NAME_DEF_LAYER , "mycontrol" , 0 , 0);

endif

CreateInfo.dwStyle = WS_VISIBLE | WS_BORDER | WS_CAPTION;
CreateInfo.dwExStyle = WS_EX_NONE;
CreateInfo.spCaption = "Hello, world";
CreateInfo.hMenu = 0;
CreateInfo.hCursor = GetSystemCursor(0);
CreateInfo.hIcon = 0;
CreateInfo.MainWindowProc = HelloWinProc;
CreateInfo.lx = 0;
CreateInfo.ty = 0;
CreateInfo.rx = 480;
CreateInfo.by = 480;
CreateInfo.iBkColor = COLOR_lightwhite;
CreateInfo.dwAddData = 0;
CreateInfo.hHosting = HWND_DESKTOP;

hMainWnd = CreateMainWindow (&CreateInfo);

if (hMainWnd == HWND_INVALID)
    return -1;

ShowWindow(hMainWnd, SW_SHOWNORMAL);

while (GetMessage(&Msg, hMainWnd)) {
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);
}

MainWindowThreadCleanup (hMainWnd);

return 0;

}

ifdef _MGRM_THREADS

include <minigui/dti.c>

endif

VincentWei commented 1 year ago

好的,收到!我们尽快处理。

VincentWei commented 1 year ago

我们在最新的 rel-5-0 分支版本上未能重现这个问题,多次执行均无法重现。请确认使用的 MiniGUI 版本。

我们这边运行的输出如下:

MSG_TIMER_START:2023-05-06 09:20:15.686.275

rec msg!!!

out if deal!!!

MSG_TIMER_START:2023-05-06 09:20:15.686.295

set time!!!

MSG_TIMER:2023-05-06 09:20:17.692.593

timer: 11!!!

定时器被触发的时间是正确的。

不过发现代码有几处不正确的用法,可以注意一下:

  1. HelloWinProc 中的 hwnd1 在其他消息分支中使用时,未被初始化。
  2. gui_input_window_proc 中不应该只在 default 分支中调用 DefaultDialogProc

修改后的代码可见:

https://github.com/VincentWei/mg-tests/blob/rel-5-0/api/timer.c

htk719809837 commented 1 year ago

你好,代码还是用的22年11月份的代码,但是最新的master分支的代码也有这个问题,注意到您这边rec msg前打印的时间09:20:15.686.275和set timer的打印时间09:20:15.686.295之间间隔太短,需要保证两秒以上就可以复现这个问题,我用的系统是:virtual box上的Ubuntu 20.04.4 LTS,3G运行内存,分配了4个内核的i7 9700处理器,如下是我运行的打印: :~/Downloads$ ./main 2023-05-05 21:53:20.022.143

rec msg!!!

out if deal!!!

2023-05-05 21:53:22.791.151

set time!!!

2023-05-05 21:53:22.802.458

timer!!!

2023-05-05 21:53:24.024.602

timer!!!

感谢提醒代码问题,后续我们会注意!

VincentWei commented 1 year ago

很奇怪,在我们使用的 Ubuntu 系统(22.04,非虚拟机)上,调整定时器的时间值取不同的值,仍然无法重现这个问题。

补充:Ubuntu 20.04 上也不能复现。可能和虚拟机有关,能否换个系统或者更新到 rel-5-0 上的最新代码看看?

htk719809837 commented 1 year ago

你好,我们这开发板上也存在这个问题的,所以在ubuntu上试了一下发现是必现的,是不是您那边ubuntu性能太好导致复现不出来呢?或者使用procs模式运行也是必现的,下面是我用您修改过的代码和rel5.0里面最新代码运行的输出:注意到start和install之间的时间间隔打印差不多有三秒,您那边可以用低配的ubuntu再尝试一下嘛

:~/Downloads$ ./main Starting test ./main... MSG_IDLE:2023-05-07 22:50:40.398.825 MSG_CLOSE:2023-05-07 22:50:41.292.350 MSG_TIMER_START:2023-05-07 22:50:41.292.376 INSTALL THE TIMER:2023-05-07 22:50:44.087.258 MSG_TIMER:2023-05-07 22:50:44.295.424 MiniGUIAppMain: Faield

但是:如果我将代码中for循环
for (int i = 0; i <= 2000000000;i++) {}中2000000000这个数字改成200,程序运行就正常了 :~/Downloads$ ./main Starting test ./main... MSG_IDLE:2023-05-07 22:52:57.087.329 MSG_CLOSE:2023-05-07 22:52:57.419.899 MSG_TIMER_START:2023-05-07 22:52:57.419.925 INSTALL THE TIMER:2023-05-07 22:52:57.419.929 MSG_TIMER:2023-05-07 22:53:00.418.756 Success

VincentWei commented 1 year ago

您的两个系统是32位还是64位的?

htk719809837 commented 1 year ago

开发板是32位系统,ubuntu为64位,但是带有部分32位的库

VincentWei commented 1 year ago

rel-5-0 分支上已修复该问题。

这个问题是由于主线程被阻塞,没有机会更新内部维护的 TickCount 计数器而导致的。在调用 SetTimer() 函数之前,调用 GetTickCount() 可以绕过该问题;或者在长时间处理业务后,先通过 PostMessage 或者 SendNotifyMessage 发送一条消息然后返回,紧接着在另一个消息中设置定时器,从而可以让程序有机会在消息循环中更新 TickCount。