initial implementation of concurrent tasks

This commit is contained in:
Vincent Wei
2021-05-21 15:53:18 +08:00
parent 87b9facc84
commit 9befaeb68a
7 changed files with 436 additions and 67 deletions
+18 -22
View File
@@ -95,7 +95,7 @@ message_string="no"
osname="unspecified"
runtime_mode="procs"
compositing_schema="yes"
nr_update_threads=0
nr_concurrent_tasks=0
virtual_window="no"
use_shmopen="no"
mgslice_use_fallback="no"
@@ -861,9 +861,9 @@ if test "x$runtime_mode" == "xprocs"; then
[ --enable-shmopen use shm_open <default=no>],
use_shmopen=$enableval)
AC_ARG_WITH(update_threads,
[ --with-update-threads=[0/1/2/4/8]],
nr_update_threads=$withval)
AC_ARG_WITH(concurrent_tasks,
[ --with-concurrent-tasks=[0/1/3/7]],
nr_concurrent_tasks=$withval)
fi
if test "x$runtime_mode" != "xths"; then
@@ -1783,33 +1783,29 @@ if test "x$surface_schema" = "xcompositing"; then
AC_DEFINE(_MGSCHEMA_COMPOSITING, 1,
[Define if use compositing schema for the current runmode])
case "$nr_update_threads" in
case "$nr_concurrent_tasks" in
1)
AC_DEFINE(_MGNR_UPDATE_THREADS, 1,
[The number of concurrent update threads.])
AC_DEFINE(_MGNR_CONCURRENT_TASKS, 1,
[The number of concurrent threads.])
;;
2)
AC_DEFINE(_MGNR_UPDATE_THREADS, 2,
[The number of concurrent update threads.])
3)
AC_DEFINE(_MGNR_CONCURRENT_TASKS, 3,
[The number of concurrent threads.])
;;
4)
AC_DEFINE(_MGNR_UPDATE_THREADS, 4,
[The number of concurrent update threads.])
;;
8)
AC_DEFINE(_MGNR_UPDATE_THREADS, 8,
[The number of concurrent update threads.])
7)
AC_DEFINE(_MGNR_CONCURRENT_TASKS, 7,
[The number of concurrent threads.])
;;
*)
AC_DEFINE(_MGNR_UPDATE_THREADS, 0,
[The number of concurrent update threads.])
AC_DEFINE(_MGNR_CONCURRENT_TASKS, 0,
[The number of concurrent threads.])
;;
esac
else
AC_DEFINE(_MGSCHEMA_SHAREDFB, 1,
[Define if use legacy schema (shared frame buffer) for the current runmode])
AC_DEFINE(_MGNR_UPDATE_THREADS, 0,
[The number of asynchronous update threads.])
AC_DEFINE(_MGNR_CONCURRENT_TASKS, 0,
[The number of concurrent threads.])
fi
if test "x$virtual_window" = "xyes"; then
@@ -2824,7 +2820,7 @@ AC_MSG_NOTICE([
* Runtime mode: ${runtime_mode}
* Virtual Window: ${virtual_window}
* Surface schema: ${surface_schema}
* Update Threads: ${nr_update_threads}
* Concurrent Tasks: ${nr_concurrent_tasks}
* Use shm_open: ${use_shmopen}
* Incore resource: ${incore_res}
* Fallback mgslice: ${mgslice_use_fallback}
+1 -1
View File
@@ -6455,7 +6455,7 @@ MG_EXPORT RES_KEY Str2Key (const char* str);
* \fn int GUIAPI GetWindowZOrder(HWND hWnd);
* \brief Get The Control ZOrder in the parent window
*
* \param hWnd Handler of a control
* \param hWnd Handle of a control
*
* \return return index of ZOrder or 0 if hWnd is a MainWindow or -1 if error
*
+2
View File
@@ -42,6 +42,8 @@ COMMON_SRCS = \
surface-shm.c \
shadow-screen.h \
shadow-screen.c \
concurrent-tasks.h \
concurrent-tasks.c \
$(NULL_FILE)
libnewgal_la_SOURCES = $(COMMON_SRCS)
+304
View File
@@ -0,0 +1,304 @@
///////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT NOTICE
//
// The following open source license statement does not apply to any
// entity in the Exception List published by FMSoft.
//
// For more information, please visit:
//
// https://www.fmsoft.cn/exception-list
//
//////////////////////////////////////////////////////////////////////////////
/*
* This file is part of MiniGUI, a mature cross-platform windowing
* and Graphics User Interface (GUI) support system for embedded systems
* and smart IoT devices.
*
* Copyright (C) 2021, Beijing FMSoft Technologies Co., Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Or,
*
* As this program is a library, any link to this program must follow
* GNU General Public License version 3 (GPLv3). If you cannot accept
* GPLv3, you need to be licensed from FMSoft.
*
* If you have got a commercial license of this program, please use it
* under the terms and conditions of the commercial license.
*
* For more information about the commercial license, please refer to
* <http://www.minigui.com/blog/minigui-licensing-policy/>.
*/
/*
** concurrent-tasks.c:
** This file implements the concurrent tasks by using POSIX threads.
**
** Current maintainer: Wei Yongming.
**
** Create date: 2021/05/21
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "common.h"
#include "minigui.h"
#include "constants.h"
#include "newgal.h"
#include <pthread.h>
#include <semaphore.h>
typedef void (*task_proc)(void* context, int loop_idx);
typedef struct _ConcurrentTasksInfo {
pthread_t pths[_MGNR_CONCURRENT_TASKS];
sem_t sem_loop;
sem_t sem_lock;
sem_t sem_sync;
int nr_loops;
task_proc proc;
void* ctxt;
} ConcurrentTasksInfo;
static ConcurrentTasksInfo *cctasks_info;
static inline int cc_sem_wait (sem_t *sem)
{
try_again:
if (sem_wait (sem) < 0) {
if (errno == EINTR)
goto try_again;
else {
_WRN_PRINTF ("Failed sem_wait: %s\n", strerror (errno));
return -1;
}
}
return 0;
}
static void* cc_task_entry (void* data)
{
int task_idx = (int)(intptr_t)data;
if (pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL))
return NULL;
do {
int loop_idx;
if (cc_sem_wait (&cctasks_info->sem_loop)) {
_ERR_PRINTF ("CCTasks: thread %d failed on sem_wait\n", task_idx);
return NULL;
}
cc_sem_wait (&cctasks_info->sem_lock);
loop_idx = cctasks_info->nr_loops++;
sem_post (&cctasks_info->sem_lock);
pthread_testcancel ();
cctasks_info->proc (cctasks_info->ctxt, loop_idx);
if (sem_post (&cctasks_info->sem_sync)) {
_ERR_PRINTF ("CCTasks: thread %d failed on sem_post\n", task_idx);
return NULL;
}
pthread_testcancel ();
} while (1);
return NULL;
}
static void split_rect (GAL_Rect* rcs, const GAL_Rect* rc, int count)
{
if (count == 1) {
rcs[0] = *rc;
}
else if (count == 2) {
rcs[0].x = rc->x;
rcs[0].y = rc->y;
rcs[0].w = rc->w;
rcs[0].h = rc->h >> 1;
rcs[1].x = rc->x;
rcs[1].y = rcs[0].y + rcs[0].h;
rcs[1].w = rc->w;
rcs[1].h = rc->h - rcs[0].h;
}
else if (count == 4) {
rcs[0].x = rc->x;
rcs[0].y = rc->y;
rcs[0].w = rc->w;
rcs[0].h = rc->h >> 2;
rcs[1].x = rc->x;
rcs[1].y = rcs[0].y+ rcs[0].h;
rcs[1].w = rc->w;
rcs[1].h = rc->h >> 2;
rcs[2].x = rc->x;
rcs[2].y = rcs[1].y+ rcs[1].h;
rcs[2].w = rc->w;
rcs[2].h = rc->h >> 2;
rcs[3].x = rc->x;
rcs[3].y = rcs[2].y+ rcs[2].h;
rcs[3].w = rc->w;
rcs[3].h = rc->h - rcs[0].h - rcs[1].h - rcs[2].h;
}
else {
assert (0);
}
}
typedef struct _ContextBlit {
GAL_Surface *src;
GAL_Surface *dst;
GAL_blit real_blit;
GAL_Rect src_rects [_MGNR_CONCURRENT_TASKS + 1];
GAL_Rect dst_rects [_MGNR_CONCURRENT_TASKS + 1];
} ContextBlit;
static void blit_proc (void* context, int loop_idx)
{
ContextBlit *ctxt = context;
if (ctxt->src_rects[loop_idx].h > 0) {
ctxt->real_blit (ctxt->src, ctxt->src_rects + loop_idx,
ctxt->dst, ctxt->dst_rects + loop_idx);
}
}
int concurrentTasks_Blit (GAL_blit real_blit,
struct GAL_Surface *src, GAL_Rect *srcrect,
struct GAL_Surface *dst, GAL_Rect *dstrect)
{
int i;
ContextBlit ctxt;
ctxt.src = src;
ctxt.dst = dst;
ctxt.real_blit = real_blit;
split_rect (ctxt.src_rects, srcrect, _MGNR_CONCURRENT_TASKS + 1);
split_rect (ctxt.dst_rects, dstrect, _MGNR_CONCURRENT_TASKS + 1);
if (_MGNR_CONCURRENT_TASKS > 0) {
cctasks_info->nr_loops = 1; // reserve 0 for the current thread
cctasks_info->proc = blit_proc;
cctasks_info->ctxt = &ctxt;
}
// wake up the concurrent task threads
for (i = 0; i < _MGNR_CONCURRENT_TASKS; i++) {
sem_post (&cctasks_info->sem_loop);
}
blit_proc (&ctxt, 0);
// wait for finish of concurrent task threads
for (i = 0; i < _MGNR_CONCURRENT_TASKS; i++) {
if (cc_sem_wait (&cctasks_info->sem_sync) < 0)
return -1;
}
return 0;
}
int concurrentTasks_Init (void)
{
int i;
if (_MGNR_CONCURRENT_TASKS <= 0)
return 0;
if ((cctasks_info = calloc (1, sizeof (ConcurrentTasksInfo))) == NULL) {
_ERR_PRINTF ("CCTasks: failed to allocate memory\n");
goto failed_sem_loop;
}
if (sem_init (&cctasks_info->sem_loop, 0, 0)) {
_ERR_PRINTF ("CCTasks: failed to create loop semaphore: %s\n",
strerror (errno));
goto failed_sem_loop;
}
if (sem_init (&cctasks_info->sem_sync, 0, 0)) {
_ERR_PRINTF ("CCTasks: failed to create sync semaphore: %s\n",
strerror (errno));
goto failed_sem_sync;
}
if (sem_init (&cctasks_info->sem_lock, 0, 1)) {
_ERR_PRINTF ("CCTasks: failed to create lock semaphore: %s\n",
strerror (errno));
goto failed_sem_lock;
}
for (i = 0; i < _MGNR_CONCURRENT_TASKS; i++) {
if (pthread_create (cctasks_info->pths + i, NULL, cc_task_entry,
(void*)(intptr_t)(i + 1))) {
_ERR_PRINTF ("CCTasks: failed to create update thread (%d): %s\n",
i, strerror (errno));
goto failed_threads;
}
}
return 0;
failed_threads:
sem_destroy (&cctasks_info->sem_lock);
failed_sem_lock:
sem_destroy (&cctasks_info->sem_sync);
failed_sem_sync:
sem_destroy (&cctasks_info->sem_loop);
failed_sem_loop:
return -1;
}
int concurrentTasks_Term (void)
{
int i;
if (_MGNR_CONCURRENT_TASKS <= 0)
return 0;
for (i = 0; i < _MGNR_CONCURRENT_TASKS; i++) {
_WRN_PRINTF ("Cancelling concurrent task thread: %d\n", i);
/* send cancel request */
pthread_cancel (cctasks_info->pths [i]);
pthread_join (cctasks_info->pths [i], NULL);
}
sem_destroy (&cctasks_info->sem_sync);
sem_destroy (&cctasks_info->sem_loop);
sem_destroy (&cctasks_info->sem_lock);
free (cctasks_info);
return 0;
}
+71
View File
@@ -0,0 +1,71 @@
///////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT NOTICE
//
// The following open source license statement does not apply to any
// entity in the Exception List published by FMSoft.
//
// For more information, please visit:
//
// https://www.fmsoft.cn/exception-list
//
//////////////////////////////////////////////////////////////////////////////
/*
* This file is part of MiniGUI, a mature cross-platform windowing
* and Graphics User Interface (GUI) support system for embedded systems
* and smart IoT devices.
*
* Copyright (C) 2002~2020, Beijing FMSoft Technologies Co., Ltd.
* Copyright (C) 1998~2002, WEI Yongming
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Or,
*
* As this program is a library, any link to this program must follow
* GNU General Public License version 3 (GPLv3). If you cannot accept
* GPLv3, you need to be licensed from FMSoft.
*
* If you have got a commercial license of this program, please use it
* under the terms and conditions of the commercial license.
*
* For more information about the commercial license, please refer to
* <http://www.minigui.com/blog/minigui-licensing-policy/>.
*/
/*
** The helpers for concurrent tasks.
**
** Current maintainer: Wei Yongming.
**
** Create date: 2021/05/21
*/
#ifndef _GAL_concurrent_tasks_h
#define _GAL_concurrent_tasks_h
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
int concurrentTasks_Init (void);
int concurrentTasks_Term (void);
int concurrentTasks_Blit (GAL_blit real_blit,
struct GAL_Surface *src, GAL_Rect *srcrect,
struct GAL_Surface *dst, GAL_Rect *dstrect);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _GAL_concurrent_tasks_h */
+32 -19
View File
@@ -304,10 +304,12 @@ int shadowScreen_BlitToReal (_THIS)
static struct UpdateThreadsInfo {
GAL_VideoDevice *device;
sem_t sem_tasks[_MGNR_UPDATE_THREADS];
sem_t sem_loop;
sem_t sem_lock;
sem_t sem_sync;
pthread_t pths[_MGNR_UPDATE_THREADS];
int nr_loops;
RECT bound;
} cuth_info;
@@ -353,25 +355,31 @@ try_again:
static void* task_do_update (void* data)
{
int idx = (int)(intptr_t)data;
int task_idx = (int)(intptr_t)data;
if (pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL))
return NULL;
do {
if (my_sem_wait (cuth_info.sem_tasks + idx - 1)) {
_ERR_PRINTF ("Update thread %d failed on sem_wait\n", idx);
int loop_idx;
if (my_sem_wait (&cuth_info.sem_loop)) {
_ERR_PRINTF ("Update thread %d failed on sem_wait\n", task_idx);
return NULL;
}
my_sem_wait (&cuth_info.sem_lock);
loop_idx = cuth_info.nr_loops++;
sem_post (&cuth_info.sem_lock);
pthread_testcancel ();
copy_lines (cuth_info.device->hidden->shadow_screen,
cuth_info.device->hidden->real_screen,
&cuth_info.bound, idx);
&cuth_info.bound, loop_idx);
if (sem_post (&cuth_info.sem_sync)) {
_ERR_PRINTF ("Update thread %d failed on sem_post\n", idx);
_ERR_PRINTF ("Update thread %d failed on sem_post\n", task_idx);
return NULL;
}
@@ -390,12 +398,10 @@ int shadowScreen_InitUpdateThreads (_THIS)
assert (this->real_screen && this->shadow_screen);
for (i = 0; i < _MGNR_UPDATE_THREADS; i++) {
if (sem_init (cuth_info.sem_tasks + i, 0, 0)) {
_ERR_PRINTF ("NEWGAL>DBLBUFF: failed to create task semaphore: %s\n",
strerror (errno));
goto failed_sem_update;
}
if (sem_init (&cuth_info.sem_loop, 0, 0)) {
_ERR_PRINTF ("NEWGAL>DBLBUFF: failed to create loop semaphore: %s\n",
strerror (errno));
goto failed_sem_update;
}
if (sem_init (&cuth_info.sem_sync, 0, 0)) {
@@ -404,6 +410,12 @@ int shadowScreen_InitUpdateThreads (_THIS)
goto failed_sem_sync;
}
if (sem_init (&cuth_info.sem_lock, 0, 1)) {
_ERR_PRINTF ("NEWGAL>DBLBUFF: failed to create lock semaphore: %s\n",
strerror (errno));
goto failed_sem_lock;
}
for (i = 0; i < _MGNR_UPDATE_THREADS; i++) {
if (pthread_create (cuth_info.pths + i, NULL, task_do_update,
(void*)(intptr_t)(i + 1))) {
@@ -417,12 +429,13 @@ int shadowScreen_InitUpdateThreads (_THIS)
return 0;
failed_threads:
sem_destroy (&cuth_info.sem_lock);
failed_sem_lock:
sem_destroy (&cuth_info.sem_sync);
failed_sem_sync:
for (i = 0; i < _MGNR_UPDATE_THREADS; i++) {
sem_destroy (cuth_info.sem_tasks + i);
}
sem_destroy (&cuth_info.sem_loop);
failed_sem_update:
return -1;
@@ -440,9 +453,8 @@ int shadowScreen_TermUpdateThreads (_THIS)
}
sem_destroy (&cuth_info.sem_sync);
for (i = 0; i < _MGNR_UPDATE_THREADS; i++) {
sem_destroy (cuth_info.sem_tasks + i);
}
sem_destroy (&cuth_info.sem_loop);
sem_destroy (&cuth_info.sem_lock);
return 0;
}
@@ -491,11 +503,12 @@ int shadowScreen_BlitToReal (_THIS)
CHECK_VERSION_RETVAL (this, -1);
cuth_info.nr_loops = 1; // reserve 0 for the current thread
cuth_info.bound = this->hidden->dirty_rc;
// wake up the concurrent update threads
for (i = 0; i < _MGNR_UPDATE_THREADS; i++) {
sem_post (cuth_info.sem_tasks + i);
sem_post (&cuth_info.sem_loop);
}
copy_lines (cuth_info.device->hidden->shadow_screen,
+8 -25
View File
@@ -59,6 +59,7 @@
#ifdef _MGRM_PROCESSES
#include <sys/mman.h> /* for munmap */
#include "concurrent-tasks.h"
#endif /* _MGRM_PROCESSES */
/* Public routines */
@@ -538,38 +539,20 @@ int GAL_LowerBlit (GAL_Surface *src, GAL_Rect *srcrect,
}
#endif
#ifdef MG_CONFIG_USE_OWN_OVERLAPPED_BITBLIT
ret = own_overlapped_bitblit(do_blit, src, srcrect, dst, dstrect);
#ifdef _MGRM_PROCESSES
// we use concurrent tasks only under MiniGUI-Processes.
concurrentTasks_Blit (do_blit, src, srcrect, dst, dstrect);
#else
# ifdef MG_CONFIG_USE_OWN_OVERLAPPED_BITBLIT
ret = own_overlapped_bitblit(do_blit, src, srcrect, dst, dstrect);
# else
ret = do_blit(src, srcrect, dst, dstrect);
#endif
#if 0
{
static int n_sw=0, n_hw=0;
if ((src->flags & GAL_HWACCEL) == GAL_HWACCEL)
{
n_hw ++;
}
else
{
n_sw ++;
}
printf("[%06u] hw/blit=%d/%d %c size=%dX%d src=(%d %d),%p dst=(%d %d),%p\n",
times(NULL) % 1000000,
n_hw, n_hw+n_sw,
do_blit == src->map->sw_blit ? 'S':'H',
srcrect->w, srcrect->h,
srcrect->x, srcrect->y, src,
dstrect->x, dstrect->y, dst
);
}
# endif
#endif
return ret;
}
int GAL_UpperBlit (GAL_Surface *src, GAL_Rect *srcrect,
GAL_Surface *dst, GAL_Rect *dstrect, DWORD op)
{