File: ThreadsafeX11Guard.cpp

package info (click to toggle)
kwave 0.7.2-5
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 9,048 kB
  • ctags: 4,906
  • sloc: cpp: 31,275; ansic: 13,111; sh: 9,511; perl: 2,724; makefile: 786; asm: 145
file content (152 lines) | stat: -rw-r--r-- 4,793 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/***************************************************************************
  ThreadsafeX11Guard.cpp -  guard for using X11 from a worker thread
			     -------------------
    begin                : Sun Jun 03 2001
    copyright            : (C) 2001 by Thomas Eschenbacher
    email                : Thomas.Eschenbacher@gmx.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <qapplication.h> // for flushX and syncX

#include "mt/Mutex.h"
#include "mt/MutexGuard.h"
#include "mt/Semaphore.h"
#include "mt/ThreadsafeX11Guard.h"

//***************************************************************************

/* static initializer for the global/static X11 lock */
Mutex ThreadsafeX11Guard::m_lock_X11;

/* static initializer for the internal/static X11 lock */
Mutex ThreadsafeX11Guard::m_internal_lock;

/* static initializer for the lock for protection of recursion and owner */
Mutex ThreadsafeX11Guard::m_lock_recursion;

/* static initializer for the thread that currently has locked X11 */
pthread_t ThreadsafeX11Guard::m_pid_owner = pthread_self();

/* static initializer for the thread that owns X11 (main thread) */
pthread_t ThreadsafeX11Guard::m_pid_x11 = pthread_self();

/* static initializer for the counter of recursive X11 locks. */
unsigned int ThreadsafeX11Guard::m_recursion_level = 0;

//***************************************************************************
ThreadsafeX11Guard::ThreadsafeX11Guard()
    :QObject(), TSS_Object(), m_sem_x11_locked(), m_sem_x11_done(),
     m_sem_x11_unlocked(), m_spx_X11_request(this, SLOT(lockX11()))
{
    if (m_pid_x11 == pthread_self()) {
	return;
    }

    {
	MutexGuard lock(m_lock_recursion);
	if (m_pid_owner == pthread_self()) {
	    // recursive enter
	    m_recursion_level++;
	    return;
	}
    }

    // lock the X11 system, no others may use it now
    m_internal_lock.lock();

    // activate the X11 thread
    m_spx_X11_request.AsyncHandler();

    // sometimes the X11 / GUI thread needs an extra wakeup
    Q_ASSERT(qApp);
    if (qApp) qApp->wakeUpGuiThread();

    // wait until the X11 thread is locked and suspended
    m_sem_x11_locked.wait();

    // now we are the only owner of X11
    {
	MutexGuard lock(m_lock_recursion);
	m_pid_owner = pthread_self();
	m_recursion_level = 1;
    }
}

//***************************************************************************
ThreadsafeX11Guard::~ThreadsafeX11Guard()
{
    if (m_pid_x11 == pthread_self()) {
	return;
    }

    {
	MutexGuard lock(m_lock_recursion);
	
	// decrease the recursion level (should always be at least 1)
	Q_ASSERT(m_recursion_level);
	if (m_recursion_level) m_recursion_level--;
	
	// break if only recursion decreased and zero not reached
	if (m_recursion_level) return;
    }

    // flush all X11 events
    QApplication::flushX();

    // let the X11 thread continue
    m_sem_x11_done.post();

    // wait until X11 is unlocked again
    m_sem_x11_unlocked.wait();

    // release the control over X11
    {
	MutexGuard lock(m_lock_recursion);
	m_pid_owner = 0;
	m_recursion_level = 0;
    }

    // sometimes the X11 / GUI thread needs an extra wakeup
    Q_ASSERT(qApp);
    if (qApp) qApp->wakeUpGuiThread();

    // release the X11 system and let others lock it
    m_internal_lock.unlock();
}

//***************************************************************************
void ThreadsafeX11Guard::lockX11()
{
    // lock the global/static X11 mutex
    m_lock_X11.lock();

    // be sure there are no other X11 transactions we interrupt
    // so better give them a chance to complete within the
    // context of our thread
    QApplication::syncX();
    QApplication::flushX();

    // notify the waiting worker thread that X11 is locked now
    m_sem_x11_locked.post();

    // suspend until the worker thread is done with X11
    m_sem_x11_done.wait();

    // unlock X11 again
    m_lock_X11.unlock();

    // notify the worker thread that X11 is available/unlocked again
    m_sem_x11_unlocked.post();
}

//***************************************************************************
//***************************************************************************