MS RFC 15:支持mapserver/mapscript的线程无关操作

日期

2006/05/01

作者

塔玛斯塞克勒斯

联系

gmail.com上的szekerest

状态

草稿

版本

尚未分配

1。概述

到目前为止,核心mapserver代码已被许多不同的应用程序模型广泛使用。MAPScript的扩展使用可能导致代码的执行由多个线程执行,从而使不同线程同时调用代码的某些部分成为可能。应用程序的线程模型可能超出了MapServer代码的控制范围,最坏的情况下,甚至应用程序的主机进程也超出了程序员的范围。例如,Web映射应用程序可以由预先存在的主机进程执行,以确保为用户请求提供服务,并通过线程池中的不同线程调用MapServer代码。在MapServer中使用进程范围的全局变量和资源可能需要通过使用锁对多个线程对变量的访问进行序列化。当前的方法使用进程范围的锁来确保对这些变量的安全访问,从而导致多线程环境中的性能下降。目前,此行为可能由use_thread compilation开关控制。

2。目的

此更改的目的是通过从代码中删除“大锁”来显著提高性能。它将显著改进同时执行多个线程的应用程序。这项活动可以为一些应用程序模型提供更可行的支持,而这些应用程序模型现在并不像Microsoft ASP.NET那样是真正针对的。通过确定受多个线程影响的代码段,可以提高代码的清晰度。

2.第2条。解决方案的一般原则

这个RFC处于一个特殊的情况,因为需要对代码的广泛领域进行审查,并且需要广泛的开发人员支持。只有对所有未解决的问题进行分类和妥善处理,提案才能达到拟议的状态。

MAPServer模块的更改应与主维护人员一致。维护人员可以保留在实施阶段提交此RFC建议的更改的权利。否则,主要作者Tamas Szekeres将负责进行更改。

稍后将列举当前实施的问题和拟议的更改。为了处理这些问题,以下选项将被视为权宜之计订购。后者是不太合适的。

2.1不更改代码(视为无锁安全)

一些进程范围的全局变量存储不变的数据(如枚举),这些变量在编译时已初始化。由于这些变量可以被多个线程同时读取,因此无需执行任何操作,甚至应该删除锁(如果存在),以确保实现线程隔离。可能重新声明为“静态常量”。

2.2保留变量,但重新考虑初始化代码

一些全局变量存储不变的数据,但在模块初始化阶段的运行时分配初始值。在这种情况下,我们应该确保在接收到后续访问之前完成初始化,以防止出现争用条件的可能性。mssetup()被指定为进行变量模块级初始化的合适位置。鼓励mapscript语言绑定在模块启动时自动提供调用mssetup。应该检查当前的锁定策略,并且应该将锁定限制在初始化代码中。

2.3重写代码以消除全局变量的需要

如果通过更改代码结构可以消除全局变量,我们将考虑进行这些更改。应该删除与这些变量相关的锁。

2.4使用线程局部变量而不是全局变量

当全局变量的使用不能轻易消除时,我们将考虑改用线程局部变量。为了实现线程局部变量,理论上我们至少要做两个选择:

  1. 通过修改变量声明,我们可以在POSIX环境中使用特定于Microsoft的“uu declspec(thread)”或“uu thread”等存储说明符。

  2. 通过使用与平台相关的TLS API(如Windows TLS或Unix pthreads实现)来实现线程本地内存的分配。

第一个不适用于MapServer,因为Microsoft实现不允许延迟加载包含用u declspec(thread)声明的变量的dll。

第二种方法是适用的,但是重写现有代码可能更困难。gdal有一个可能被接管的示例实现。

2.5不更改代码(用锁标记为安全,稍后将重新考虑)

一些mapserver代码和/或相关的外部库可能不容易修改,在实现的这个阶段将受到锁的保护。这些问题将保持开放状态,稍后可能会重新考虑线程隔离的解决方案。打开的问题将与mapserver源文件和线程安全常见问题解答一起枚举和记录。

http://mapserver.gis.umn.edu/docs/faq/thread_safety

2.6不更改代码(标记为不安全,将被否决和不支持)

缺少对现有代码部分的支持可能会妨碍轻松地更改代码。在这个实现阶段,代码的这些部分不会被修改。这些问题将保持开放,稍后可能会重新考虑。打开的问题将与mapserver源文件和线程安全常见问题一起枚举和记录。

三。mapserver/mapscript代码的问题

本章将列举现有代码的章节,应根据前面提到的选项重新考虑。根据开发人员的工作,行数可能略有变化。

epplib.c(47):static int REVERSE; /* set to 1 on bigendian machines */

在eppreset中使用以下代码union long i;char c[4];u;u.i=1设置此变量;reverse=(u.c[0]==0);应在初始化期间或由makefile设置为预定义常量。另外,mapserver配置脚本可能包括字节顺序检测。这个常数可能被其他模块使用。

这个常数仅限于epplib.c…

mapcpl.c(57):static char     szStaticResult[MS_PATH_BUF_SIZE];

法兰克:事实上,这段代码是从gdal继承过来的,后来在gdal中重新构建的。看起来msgetbasename()只在少数地方使用,我们应该重新构建它们以避免使用静态缓冲区。

maperror.c(110):static char *ms_errorCodes[MS_NUMERRORCODES] = {"",
maperror.c(154):    static errorObj ms_error = {MS_NOERR, "", "", NULL};
maperror.c(169):static te_info_t *error_list = NULL;
maperror.c(552):  static char version[1024];
maperror.c(651):    static char nonblocking_set = 0;

TODO

mapfile.c(184):static char *msUnits[8]={"INCHES", "FEET", "MILES", "METERS", "KILOMETERS", "DD",  "PIXELS", "PERCENTAGES"};
mapfile.c(185):static char *msLayerTypes[8]={"POINT", "LINE", "POLYGON", "RASTER", "ANNOTATION", "QUERY", "CIRCLE", "TILEINDEX"};
mapfile.c(186):char *msPositionsText[MS_POSITIONS_LENGTH] = {"UL", "LR", "UR", "LL", "CR", "CL", "UC", "LC", "CC", "AUTO", "XY", "FOLLOW"}; /* msLabelPositions[] also used in mapsymbols.c (not static) */
mapfile.c(187):static char *msBitmapFontSizes[5]={"TINY", "SMALL", "MEDIUM", "LARGE", "GIANT"};
mapfile.c(188):static char *msQueryMapStyles[4]={"NORMAL", "HILITE", "SELECTED", "INVERTED"};
mapfile.c(189):static char *msStatus[5]={"OFF", "ON", "DEFAULT", "EMBED"};
mapfile.c(191):static char *msTrueFalse[2]={"FALSE", "TRUE"};
mapfile.c(193):static char *msJoinType[2]={"ONE-TO-ONE", "ONE-TO-MANY"};

以上均为常量静态数据,按2.1处理。

mapgd.c(239):static unsigned char PNGsig[8] = {137, 80, 78, 71, 13, 10, 26, 10}; /* 89 50 4E 47 0D 0A 1A 0A hex */
mapgd.c(240):static unsigned char JPEGsig[3] = {255, 216, 255}; /* FF D8 FF hex */
mapgd.c(911):  static gdPoint points[38];
mapgd.c(2428):  static double last_style_size;
mapgd.c(2719):  static int styleIndex, styleVis;
mapgd.c(2720):  static double styleSize=0, styleCoef=0, last_style_size=-1;
mapgd.c(2721):  static int last_style_c=-1, last_style_stylelength=-1, last_styleVis=0;

TODO

mapgdal.c(141):static int    bGDALInitialized = 0;

按2.2处理。(启动初始化)

mapgeos.cpp(98):GeometryFactory *gf=NULL;

按2.2处理。(启动初始化)

maphttp.c(140):static int gbCurlInitialized = MS_FALSE;

按2.2处理。(启动初始化)

mapimagemap.c(141):static char *layerlist=NULL;
mapimagemap.c(142):static int layersize=0;
mapimagemap.c(143):static pString imgStr, layerStr={ &layerlist, &layersize, 0 };
mapimagemap.c(146):static const char *polyHrefFmt, *polyMOverFmt, *polyMOutFmt;
mapimagemap.c(147):static const char *symbolHrefFmt, *symbolMOverFmt, *symbolMOutFmt;
mapimagemap.c(148):static const char *mapName;
mapimagemap.c(150):static int suppressEmpty=0;
mapimagemap.c(229):static int lastcolor=-1;
mapimagemap.c(273):static char* lname;
mapimagemap.c(274):static int dxf;

Imagemap支持尚未广泛使用,目前将按2.6处理。仍在等待评论。

mapio.c(67):static int is_msIO_initialized = MS_FALSE;
mapio.c(69):static msIOContext default_stdin_context;
mapio.c(70):static msIOContext default_stdout_context;
mapio.c(71):static msIOContext default_stderr_context;
mapio.c(73):static msIOContext current_stdin_context;
mapio.c(74):static msIOContext current_stdout_context;
mapio.c(75):static msIOContext current_stderr_context;

Frankw:目前只有一组用于IO的进程范围的“IO处理程序”。这几乎肯定需要在某一点上进行更改,以便以某种方式成为局部线程。我希望在今年春天从mapscript访问可重定向的OWS服务时解决这个问题。

maplexer.c(220):static YY_BUFFER_STATE yy_current_buffer = 0;
maplexer.c(230):static char yy_hold_char;
maplexer.c(232):static int yy_n_chars;         /* number of characters read into yy_ch_buf */
maplexer.c(238):static char *yy_c_buf_p = (char *) 0;
maplexer.c(239):static int yy_init = 1;                /* whether we need to initialize */
maplexer.c(240):static int yy_start = 0;       /* start state number */
maplexer.c(245):static int yy_did_buffer_switch_on_eof;
maplexer.c(306):static yyconst short int yy_accept[2442] =
maplexer.c(579):static yyconst int yy_ec[256] =
maplexer.c(611):static yyconst int yy_meta[52] =
maplexer.c(621):static yyconst short int yy_base[2456] =
maplexer.c(895):static yyconst short int yy_def[2456] =
maplexer.c(1169):static yyconst short int yy_nxt[2776] =
maplexer.c(1478):static yyconst short int yy_chk[2776] =
maplexer.c(1787):static yy_state_type yy_last_accepting_state;
maplexer.c(1788):static char *yy_last_accepting_cpos;
maplexer.c(1886):static int yy_start_stack_ptr = 0;
maplexer.c(1887):static int yy_start_stack_depth = 0;
maplexer.c(1888):static int *yy_start_stack = 0;

根据史蒂夫的建议,我们将通过适当调用新版本的flex和bison来解决这个问题。

mapmygis.c(245):static int gBYTE_ORDER = 0;

TODO

mapogcsos.c(1974):    static char *request=NULL, *service=NULL;

TODO

mapogr.cpp(840):static int bOGRDriversRegistered = MS_FALSE;

按2.2处理。(启动初始化)

mapows.c(1647):  static char epsgCode[20] ="";

TODO

mapparser.c(282):static const unsigned char yytranslate[] =
mapparser.c(317):static const unsigned char yyprhs[] =
mapparser.c(328):static const yysigned_char yyrhs[] =
mapparser.c(354):static const unsigned short yyrline[] =
mapparser.c(368):static const char *const yytname[] =
mapparser.c(381):static const unsigned short yytoknum[] =
mapparser.c(391):static const unsigned char yyr1[] =
mapparser.c(402):static const unsigned char yyr2[] =
mapparser.c(415):static const unsigned char yydefact[] =
mapparser.c(431):static const yysigned_char yydefgoto[] =
mapparser.c(439):static const short yypact[] =
mapparser.c(455):static const yysigned_char yypgoto[] =
mapparser.c(465):static const unsigned char yytable[] =
mapparser.c(491):static const yysigned_char yycheck[] =
mapparser.c(519):static const unsigned char yystos[] =

根据史蒂夫的建议,我们将通过适当调用新版本的flex和bison来解决这个问题。

mappluginlayer.c(46):static VTFactoryObj gVirtualTableFactory = {MS_MAXLAYERS, 0, {NULL}};

TODO

mappool.c(206):static int connectionCount = 0;
mappool.c(207):static int connectionMax = 0;
mappool.c(208):static connectionObj *connections = NULL;

TODO

mapproject.c(889):static char *ms_proj_lib = NULL;
mapproject.c(890):static char *last_filename = NULL;
mapproject.c(918):    static int finder_installed = 0;

TODO

mapscale.c(68):static char *unitText[5]={"in", "ft", "mi", "m", "km"};
mapscale.c(69):double inchesPerUnit[6]={1, 12, 63360.0, 39.3701, 39370.1, 4374754};

常量静态数据,按2.1处理。

mapsde.c(216):static int lcacheCount = 0;
mapsde.c(217):static int lcacheMax = 0;
mapsde.c(218):static layerId *lcache = NULL;

TODO

mapserv.c(1196):        static int nRequestCounter = 1;

考虑无锁安全(2.1)。这个 MAPSERV公司 应用程序不涉及多个线程。

mapshape.c(70):static int      bBigEndian;

应按epplib.c(47)处理:静态int反向;

mapsvg.c(164):    static char epsgCode[20] ="";
mapsvg.c(165):    static char *value;

TODO

mapswf.c(97):static char gszFilename[128];
mapswf.c(98):static char gszAction[256];
mapswf.c(99):static char gszTmp[256];

TODO

mapsymbol.c(154):static char *msCapsJoinsCorners[7]={"NONE", "BEVEL", "BUTT", "MITER", "ROUND", "SQUARE", "TRIANGLE"};

常量静态数据,按2.1处理。

mapthread.c(196):static int thread_debug = 0;
mapthread.c(198):static char *lock_names[] =

常量静态数据,按2.1处理。

mapthread.c(213):static int mutexes_initialized = 0;
mapthread.c(214):static pthread_mutex_t mutex_locks[TLOCK_MAX];
mapthread.c(223):    static pthread_mutex_t core_lock = PTHREAD_MUTEX_INITIALIZER;
mapthread.c(294):static int mutexes_initialized = 0;
mapthread.c(295):static HANDLE mutex_locks[TLOCK_MAX];
mapthread.c(305):    static HANDLE core_lock = NULL;

按2.2处理。(启动初始化)。在msthreadinit()中初始化;

maputil.c(1068):static int tmpCount = 0;
maputil.c(1069):static char *ForcedTmpBase = NULL;

TODO

mapwms.c(252):static char *wms_exception_format=NULL;

TODO

md5c.c(58):static unsigned char PADDING[64] = {

常量静态数据,按2.1处理。

strptime.c(43):static const char *abb_weekdays[] = {
strptime.c(54):static const char *full_weekdays[] = {
strptime.c(65):static const char *abb_month[] = {
strptime.c(81):static const char *full_month[] = {
strptime.c(97):static const char *ampm[] = {

常量静态数据,按2.1处理。

gdft\gdkanji.c(103):   static int whatcode;
gdft\gdkanji.c(403):   static unsigned char tmp[BUFSIZ];
gdft\gdkanji.c(489):   static unsigned char tmp_dest[BUFSIZ];

sdl:gdft及其内容不是mapserver发行版的一部分。

gdft\gdttf.c(712):  static gdCache_head_t *tweenColorCache=NULL; /****** set up tweenColorCache on first call ************/
gdft\gdttf.c(852):  static gdCache_head_t      *fontCache=NULL; /****** initialize font engine on first call ************/
gdft\gdttf.c(853):  static TT_Engine   engine;

sdl:gdft及其内容不是mapserver发行版的一部分。

gdft\jisx0208.h(7):static unsigned short UnicodeTbl[][94] = {

sdl:gdft及其内容不是mapserver发行版的一部分。

mapscript\csharp\csmodule.i(32):               static $moduleHelper()
mapscript\csharp\csmodule.i(41):       static $moduleHelper the$moduleHelper = new $moduleHelper();

初始化启动代码。

mapscript\php3\mapscript_i.c(223):    static int i=0;
mapscript\php3\mapscript_i.c(1137):        static char pszFieldName[1000];

TODO

mapscript\php3\php_mapscript.c(789):static int le_msmap;
mapscript\php3\php_mapscript.c(790):static int le_mslayer;
mapscript\php3\php_mapscript.c(791):static int le_msimg;
mapscript\php3\php_mapscript.c(792):static int le_msclass;
mapscript\php3\php_mapscript.c(793):static int le_mslabel;
mapscript\php3\php_mapscript.c(794):static int le_mscolor;
mapscript\php3\php_mapscript.c(795):static int le_msrect_new;
mapscript\php3\php_mapscript.c(796):static int le_msrect_ref;
mapscript\php3\php_mapscript.c(797):static int le_mspoint_new;
mapscript\php3\php_mapscript.c(798):static int le_mspoint_ref;
mapscript\php3\php_mapscript.c(799):static int le_msline_new;
mapscript\php3\php_mapscript.c(800):static int le_msline_ref;
mapscript\php3\php_mapscript.c(801):static int le_msshape_new;
mapscript\php3\php_mapscript.c(802):static int le_msshape_ref;
mapscript\php3\php_mapscript.c(803):static int le_msshapefile;
mapscript\php3\php_mapscript.c(804):static int le_msweb;
mapscript\php3\php_mapscript.c(805):static int le_msrefmap;
mapscript\php3\php_mapscript.c(806):static int le_msprojection_new;
mapscript\php3\php_mapscript.c(807):static int le_msprojection_ref;
mapscript\php3\php_mapscript.c(808):static int le_msscalebar;
mapscript\php3\php_mapscript.c(809):static int le_mslegend;
mapscript\php3\php_mapscript.c(810):static int le_msstyle;
mapscript\php3\php_mapscript.c(811):static int le_msoutputformat;
mapscript\php3\php_mapscript.c(812):static int le_msgrid;
mapscript\php3\php_mapscript.c(813):static int le_mserror_ref;
mapscript\php3\php_mapscript.c(814):static int le_mslabelcache;
mapscript\php3\php_mapscript.c(815):static int le_mssymbol;
mapscript\php3\php_mapscript.c(816):static int le_msquerymap;
mapscript\php3\php_mapscript.c(857):static zend_class_entry *map_class_entry_ptr;
mapscript\php3\php_mapscript.c(858):static zend_class_entry *img_class_entry_ptr;
mapscript\php3\php_mapscript.c(859):static zend_class_entry *rect_class_entry_ptr;
mapscript\php3\php_mapscript.c(860):static zend_class_entry *color_class_entry_ptr;
mapscript\php3\php_mapscript.c(861):static zend_class_entry *web_class_entry_ptr;
mapscript\php3\php_mapscript.c(862):static zend_class_entry *reference_class_entry_ptr;
mapscript\php3\php_mapscript.c(863):static zend_class_entry *layer_class_entry_ptr;
mapscript\php3\php_mapscript.c(864):static zend_class_entry *label_class_entry_ptr;
mapscript\php3\php_mapscript.c(865):static zend_class_entry *class_class_entry_ptr;
mapscript\php3\php_mapscript.c(866):static zend_class_entry *point_class_entry_ptr;
mapscript\php3\php_mapscript.c(867):static zend_class_entry *line_class_entry_ptr;
mapscript\php3\php_mapscript.c(868):static zend_class_entry *shape_class_entry_ptr;
mapscript\php3\php_mapscript.c(869):static zend_class_entry *shapefile_class_entry_ptr;
mapscript\php3\php_mapscript.c(870):static zend_class_entry *projection_class_entry_ptr;
mapscript\php3\php_mapscript.c(871):static zend_class_entry *scalebar_class_entry_ptr;
mapscript\php3\php_mapscript.c(872):static zend_class_entry *legend_class_entry_ptr;
mapscript\php3\php_mapscript.c(873):static zend_class_entry *style_class_entry_ptr;
mapscript\php3\php_mapscript.c(874):static zend_class_entry *outputformat_class_entry_ptr;
mapscript\php3\php_mapscript.c(875):static zend_class_entry *grid_class_entry_ptr;
mapscript\php3\php_mapscript.c(876):static zend_class_entry *error_class_entry_ptr;
mapscript\php3\php_mapscript.c(877):static zend_class_entry *labelcache_class_entry_ptr;
mapscript\php3\php_mapscript.c(878):static zend_class_entry *symbol_class_entry_ptr;
mapscript\php3\php_mapscript.c(879):static zend_class_entry *querymap_class_entry_ptr;
mapscript\php3\php_mapscript.c(891):static unsigned char one_arg_force_ref[] =
mapscript\php3\php_mapscript.c(893):static unsigned char two_args_first_arg_force_ref[] =

TODO

mapscript\php3\php_proj.c(196):static zend_class_entry *proj_class_entry_ptr;
mapscript\php3\php_proj.c(200):static int le_projobj;
mapscript\php3\php_proj.c(294):static long _php_proj_build_proj_object(PJ *pj,

TODO

mapscript\swiginc\dbfinfo.i(40):        static char pszFieldName[1000];

TODO

mapscript\swiginc\map.i(204):    static int i=0;

TODO

4。未来发展的考虑

开发人员应该记住,他们的代码可以由多个线程同时调用。将来将不鼓励使用在运行时修改的进程范围全局变量。如果使用不可避免,则应根据使用线程常量适当地使用互斥来保护变量。

5。向后兼容性问题

这是二进制和源代码级别的向后不兼容更改。可以保持mapscript接口的兼容性。

臭虫识别码

以下bug被添加为收集开发人员的主要源

和用户响应。然而,MAPServer用户和开发人员列表也将受到监控,主作者将相应地更新RFC。

臭虫1764

投票历史

仍不建议投票