wall_john 发表于 2005-9-20 13:45:51

kaffeine crash掉的原因即补丁

kaffeine crash掉的原因即补丁

关于XInitThreads、XLockDisplay、XUnlockDisplay三个函数

XInitThreads是使X窗口程序执行多线程处理的初始化函数,只有调用了该
函数在程序中才能使执行XLockDisplay、XUnlockDisplay有效,后面的这两
个函数是在对打开的一个Display所做的多线程处理中锁定和解锁操作。

Kaffeine程序因为调用了xine-libs,后者在多处处理时调用了多线程锁,即
上面的XLockDisplay、XUnlockDisplay这两个函数处理,所以Kaffeine必须
调用Xlib中的多线程处理,--with-xinit-workaround这个参数只是在kaffeine
主程序的main.c的main函数中绕过开始时的XInitThreads调用,但其实内部
还是调用了XInitThreads函数,KAFFEINESRC/kaffeine/player-part/kxinewidget.cpp
中函数KXineWidget::initXine()就直接调用了该函数。而且--with-xinit-workaround
配置选项根本就有问题,因为XInitThreads规定了必须在程序开始时未调用
任何Xlib中的函数时调用,所以用该选项时即使不在konqueror中调用也会报错,
看看man XInitThreads,如下说明:

XInitThreads(3X11)            XLIB FUNCTIONS            XInitThreads(3X11)

NAME
       XInitThreads, XLockDisplay, XUnlockDisplay - multi-threading support

SYNTAX
       Status XInitThreads(void);

       void XLockDisplay(Display *display);

       void XUnlockDisplay(Display *display);

ARGUMENTS
       display   Specifies the connection to the X server.

DESCRIPTION
       The XInitThreads function initializes Xlib support for concurrent
       threads.This function must be the first Xlib function a multi-          <--这里 说明必须最先调用XInitThreads
       threaded program calls, and it must complete before any other Xlib call
       is made.This function returns a nonzero status if initialization was
       successful; otherwise, it returns zero.On systems that do not support
       threads, this function always returns zero.


       It is only necessary to call this function if multiple threads might      <--这里 提到可以自己实现类似的同步机制
       use Xlib concurrently.If all calls to Xlib functions are protected by         我试过了,但因为xine-libs中默认
       some other access mechanism (for example, a mutual exclusion lock in a            使用XLockDisplay、XUnlockDisplay
       toolkit or through explicit client programming), Xlib thread initial-             做多线程同步,所以即算改了kaffeine
       ization is not required.It is recommended that single-threaded pro-             也没用,xine-libs不使用XTHREADS
       grams not call this function.                                                   就会导致内部机制出错。
                                                                                       所以无可奈何,只能在x11中想办法
       The XLockDisplay function locks out all other threads from using the
       specified display.Other threads attempting to use the display will
       block until the display is unlocked by this thread.Nested calls to
       XLockDisplay work correctly; the display will not actually be unlocked
       until XUnlockDisplay has been called the same number of times as XLock-
       Display.This function has no effect unless Xlib was successfully ini-
       tialized for threads using XInitThreads.

       The XUnlockDisplay function allows other threads to use the specified
       display again.Any threads that have blocked on the display are
       allowed to continue.Nested locking works correctly; if XLockDisplay
       has been called multiple times by a thread, then XUnlockDisplay must be
       called an equal number of times before the display is actually
       unlocked.This function has no effect unless Xlib was successfully
       initialized for threads using XInitThreads.

SEE ALSO
       Xlib - C Language X Interface

让我们分析一下为什么XInitThreads必须在程序的开始调用,总结一下修改方案。

先看看/usr/src/mBuild/BUILD/xorg-x11-6.8.2/xc/lib/X11/locking.c中代码

#include "Xlibint.h"
#undef _XLockMutex             //将头文件Xlibint.h中定义的_XLockMutex清除
#undef _XUnlockMutex         //Xlibint.h头文件中定义为:
#undef _XCreateMutex         //#define _XLockMutex(lock)                if (_XLockMutex_fn) (*_XLockMutex_fn)(lock)
#undef _XFreeMutex             //_XLockMutex_fn为函数指针,默认为0,所以默认不会做任何操作
                               //但XInitThreads之后会被赋值就不是空操作了,见下面。这四个宏都有相似定义
Status XInitThreads()
{
    if (_Xglobal_lock)
        return 1;
#ifdef xthread_init
    xthread_init();                /* return value? */
#endif
    if (!(global_lock.lock = xmutex_malloc()))
        return 0;
    if (!(i18n_lock.lock = xmutex_malloc())) {
        xmutex_free(global_lock.lock);
        global_lock.lock = NULL;
        return 0;
    }
    _Xglobal_lock = &global_lock;
    xmutex_init(_Xglobal_lock->lock);
    xmutex_set_name(_Xglobal_lock->lock, "Xlib global");
    _Xi18n_lock = &i18n_lock;
    xmutex_init(_Xi18n_lock->lock);
    xmutex_set_name(_Xi18n_lock->lock, "Xlib i18n");
    _XLockMutex_fn = _XLockMutex;                  //注意这里将_XLockMutex付给函数指针_XLockMutex_fn
    _XUnlockMutex_fn = _XUnlockMutex;                //因为上面将_XLockMutex清除了,所以使用本文件中的
    _XCreateMutex_fn = _XCreateMutex;                //该同名函数,见下面。其他四个宏定义相似
    _XFreeMutex_fn = _XFreeMutex;
    _XInitDisplayLock_fn = _XInitDisplayLock;
    _XFreeDisplayLock_fn = _XFreeDisplayLock;
    _Xthread_self_fn = _Xthread_self;

#ifdef XTHREADS_WARN
#ifdef XTHREADS_DEBUG
    setlinebuf(stdout);                /* for debugging messages */
#endif
#endif

    return 1;
}

#if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE)
static void _XLockMutex(lip,file,line)
    LockInfoPtr lip;
    char* file;
    int line;
#else
static void _XLockMutex(
    LockInfoPtr lip)
#endif
{
    xmutex_lock(lip->lock);                        //xmutex_lock最终会调用pthread_mutex_lock
}

所以当XInitThreads之后_XLockMutex实际调用的是上面这个函数。

下面是kaffeine在konqueror中报错时,我想办法打印出的堆栈(倒着看,“//”后面为注释)
#40x00991aa2 in pthread_mutex_lock () from /lib/libpthread.so.0
#50x067b695f in _XLockMutex () from /usr/X11R6/lib/libX11.so.6
//这里调用_XlockMutex(&display->db->linfo)时出现错误
#60x067a1d92 in XrmDestroyDatabase () from /usr/X11R6/lib/libX11.so.6      
//这里释放资源文件,该结构display->db由qapplicaion_x11.cpp
//中函数qt_init_internal 读取 char *dpi_str = XGetDefault(appDpy, "Xft", "dpi")
//即读取xrdb中关于Xft字体dpi信息时创建的。
#70x0678998d in _XFreeDisplayStructure () from /usr/X11R6/lib/libX11.so.6
#80x0677695c in XCloseDisplay () from /usr/X11R6/lib/libX11.so.6
#90x0499af1e in qt_cleanup () from /home/wj/kde3.5-alpha1/lib/libqt-mt.so.3
#10 0x04a13020 in QApplication::~QApplication$base ()
   from /home/wj/kde3.5-alpha1/lib/libqt-mt.so.3
#11 0x01143639 in KApplication::~KApplication$base ()
   from /home/wj/kde3.5-alpha1/lib/libkdecore.so.4
#12 0x002881a8 in kdemain ()
   from /home/wj/kde3.5-alpha1/lib/libkdeinit_konqueror.so
#13 0x080486be in main ()

_XLockMutex时报空指针错误
原因是在konqueror启动是并没有调用XInitThreads函数,所以_XCreateMutex
时是空操作,即根本没有创建mutex结构,但在
/usr/src/mBuild/BUILD/xorg-x11-6.8.2/xc/lib/X11/Xrm.c函数NewDatabase(void)
(由上面提到的XGetDefault调用)中确分配了内存:
static XrmDatabase NewDatabase(void)
{
    register XrmDatabase db;

    db = (XrmDatabase) Xmalloc(sizeof(XrmHashBucketRec)); //这里分配了内存
    if (db) {
        _XCreateMutex(&db->linfo);                            //没有调用XInitThreads函数时,这里是空操作
        db->table = (NTable)NULL;                           //所以db->linfo这个mutex结构根本没初始化
        db->mbstate = (XPointer)NULL;
#ifdef _XP_PRINT_SERVER_
        db->methods = NULL;
#else
        db->methods = _XrmInitParseInfo(&db->mbstate);
#endif
        if (!db->methods)
          db->methods = &mb_methods;
    }
    return db;
}
如果我们在整个的操作过程中没有调用XInitThreads函数的话,是不会出问题的
因为_XLockMutex没有初始化,只是个空操作。

void XrmDestroyDatabase(
    XrmDatabase   db)
{
    register NTable table, next;

    if (db) {
        _XLockMutex(&db->linfo);         //如果没有调用XInitThreads这里是空操作。
        for (next = db->table; (table = next); ) {
          next = table->next;
          if (table->leaf)
                DestroyLTable((LTable)table);
          else
                DestroyNTable(table);
        }
        _XFreeMutex(&db->linfo);      //如果没有调用XInitThreads这里是空操作。
        (*db->methods->destroy)(db->mbstate);
        Xfree((char *)db);
    }
}

但因为上面的KXineWidget::initXine()中会在中途调用XInitThreads函数,所以这
个操作被设定为/usr/src/mBuild/BUILD/xorg-x11-6.8.2/xc/lib/X11/locking.c
中_XLockMutex函数,该函数最终调用pthread_mutex_lock去锁定一个根本没初始化
的mutex结构,所以会出现上面堆栈打印出的错误。

现在的处境是,konqueror程序启动时不会最先帮我们调用XInitThreads函数,但是
Xorg的帮助中明白的告诉我们必须要最先调用。我最终采取了修改xorg-x11中代码的
方法,我觉得这始终是xorg-x11的错,通过几个小小的调整就能解决这个问题,而且
这也是xorg-x11必须要解决的问题,否则任何程序在中途调用XInitThreads都会报错,
看看下面的补丁我将进一步阐明原因:

--- xorg-x11-6.8.2/xc/lib/X11/locking.c.orig        2005-09-20 00:52:24.000000000 -0400
+++ xorg-x11-6.8.2/xc/lib/X11/locking.c        2005-09-20 00:57:16.000000000 -0400
@@ -92,7 +92,9 @@
   LockInfoPtr lip)
#endif
{
-    xmutex_lock(lip->lock);                   //原因一:为什么在调用_XLockMutex时不做是否为空指针的判断
+    if (lip != 0 && lip->lock != 0) {         //害得我们的小kaffeine老是报错
+      xmutex_lock(lip->lock);
+    }
}

#if defined(XTHREADS_WARN) || defined(XTHREADS_FILE_LINE)
@@ -105,7 +107,9 @@
   LockInfoPtr lip)
#endif
{
-    xmutex_unlock(lip->lock);               //原因二:为什么在调用_XUnlockMutex时不做是否为空指针的判断
+    if (lip != 0 && lip->lock != 0) {       //害得我们的wine-libs无法运行
+      xmutex_unlock(lip->lock);
+    }
}

static void _XCreateMutex(
@@ -121,8 +125,10 @@
static void _XFreeMutex(
   LockInfoPtr lip)
{
-    xmutex_clear(lip->lock);               //原因三:为什么在调用_XFreeMutex时不做是否为空指针的判断
-    xmutex_free(lip->lock);                //害得我自己一顿好找
+    if (lip != 0 && lip->lock != 0) {
+      xmutex_clear(lip->lock);
+      xmutex_free(lip->lock);
+    }
}

#ifdef XTHREADS_WARN
--- xorg-x11-6.8.2/xc/lib/X11/Xrm.c.orig        2005-09-20 01:30:28.000000000 -0400
+++ xorg-x11-6.8.2/xc/lib/X11/Xrm.c        2005-09-20 01:32:30.000000000 -0400
@@ -497,6 +497,7 @@

   db = (XrmDatabase) Xmalloc(sizeof(XrmHashBucketRec));
   if (db) {
+    db->linfo.lock = 0;                  //原因四:为什么在分配内存时不调用memset清零。(因为堆内存是
        _XCreateMutex(&db->linfo);            //随机分配的,初始不一定为0,不清零的话上面的判断根本就没用)
        db->table = (NTable)NULL;
        db->mbstate = (XPointer)NULL;

:)该补丁在附件中,要patch xorg-x11,编译kaffeine时用--with-xinit-workaround
选项,打了上面补丁不管XInitThreads在哪里调用都没问题,之所以还要绕过开始时调用
XInitThreads是因为输入法的问题,其实在程序中最好不要用XTHREADS处理,像qt那样自
己写QMutex或QSemaphore最多在加个QWaitCondition 就行了,xine-libs偏偏不这样,去
依靠xorg-x11,具体原因还不好说,还要看他的代码:(

hohoxu_hao115 发表于 2005-9-20 14:09:16

Good work!

cjacker 发表于 2005-9-20 14:33:42

惭愧惭愧,是我偷懒了。

仅仅是截断了qt的调用,明明知道是错的,还是作了。

sunmoon1997 发表于 2005-9-20 14:39:41

发到 xorg 的 ml 上?

冰寻 发表于 2005-9-20 20:21:09

哈哈 看来高手终于出手了

dukeduan 发表于 2005-9-20 22:45:44

呵呵,来瞻仰一下高人。
:mrgreen:

KanKer 发表于 2005-9-21 08:01:59

太精彩了,辛苦了,wall_john:P

demonlj 发表于 2005-9-21 08:39:30

漂亮啊
页: [1]
查看完整版本: kaffeine crash掉的原因即补丁