summaryrefslogtreecommitdiff
path: root/gtk/gtkobject-support.c
blob: 9666d1875a7c1caaa58f84f525533191e9d56b39 (plain)
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
/* -*- Mode: C; c-basic-offset: 4 -*- */

/* this module provides some of the base functionality of the GtkObject
 * wrapper system */

#include "pygtk-private.h"

/* ----------------- Thread stuff -------------------- */
/* The threading hacks are based on ones supplied by Duncan Grisby
 * of AT&T Labs Cambridge.  Since then they have been modified a bit. */

/* The threading code has been enhanced to be a little better with multiple
 * threads accessing GTK+.  Here are some notes on the changes by
 * Paul Fisher:
 *
 * If threading is enabled, we create a recursive version of Python's
 * global interpreter mutex using TSD.  This scheme makes it possible,
 * although rather hackish, for any thread to make a call into PyGTK,
 * as long as the GDK lock is held (that is, Python code is wrapped
 * around a threads_{enter,leave} pair).
 *
 * A viable alternative would be to wrap each and every GTK call, at
 * the Python/C level, with Py_{BEGIN,END}_ALLOW_THREADS.  However,
 * given the nature of Python threading, this option is not
 * particularly appealing.
 */


#ifdef ENABLE_PYGTK_THREADING
static GStaticPrivate pythreadstate_key = G_STATIC_PRIVATE_INIT;
static GStaticPrivate counter_key = G_STATIC_PRIVATE_INIT;

/* The global Python lock will be grabbed by Python when entering a
 * Python/C function; thus, the initial lock count will always be one.
 */
#  define INITIAL_LOCK_COUNT 1
#  define PyGTK_BLOCK_THREADS                                              \
   {                                                                       \
     gint counter = GPOINTER_TO_INT(g_static_private_get(&counter_key));   \
     if (counter == -INITIAL_LOCK_COUNT) {                                 \
       PyThreadState *_save;                                               \
       _save = g_static_private_get(&pythreadstate_key);                   \
       Py_BLOCK_THREADS;                                                   \
     }                                                                     \
     counter++;                                                            \
     g_static_private_set(&counter_key, GINT_TO_POINTER(counter), NULL);   \
   }

#  define PyGTK_UNBLOCK_THREADS                                            \
   {                                                                       \
     gint counter = GPOINTER_TO_INT(g_static_private_get(&counter_key));   \
     counter--;                                                            \
     if (counter == -INITIAL_LOCK_COUNT) {                                 \
       PyThreadState *_save;                                               \
       Py_UNBLOCK_THREADS;                                                 \
       g_static_private_set(&pythreadstate_key, _save, NULL);              \
     }                                                                     \
     g_static_private_set(&counter_key, GINT_TO_POINTER(counter), NULL);   \
   }


#else /* !WITH_THREADS */
#  define PyGTK_BLOCK_THREADS
#  define PyGTK_UNBLOCK_THREADS
#endif

void pygtk_block_threads(void) { PyGTK_BLOCK_THREADS }
void pygtk_unblock_threads(void) { PyGTK_UNBLOCK_THREADS }

/* ------------------- object support */

void
pygtk_destroy_notify(gpointer user_data)
{
    PyObject *obj = (PyObject *)user_data;

    PyGTK_BLOCK_THREADS
    Py_DECREF(obj);
    PyGTK_UNBLOCK_THREADS
}


/* used for timeout and idle functions */
void
pygtk_handler_marshal(gpointer a, PyObject *func, int nargs, GtkArg *args)
{
    PyObject *ret;

    PyGTK_BLOCK_THREADS

    if (PyTuple_Check(func))
	ret = PyObject_CallObject(PyTuple_GetItem(func, 0),
				  PyTuple_GetItem(func, 1));
    else
	ret = PyObject_CallObject(func, NULL);
    if (ret == NULL) {
	if (PyGtk_FatalExceptions)
	    gtk_main_quit();
	else {
	    PyErr_Print();
	    PyErr_Clear();
	}
	*GTK_RETLOC_BOOL(args[0]) = FALSE;
	PyGTK_UNBLOCK_THREADS
	    return;
    }
    if (ret == Py_None || (PyInt_Check(ret) && PyInt_AsLong(ret) == 0))
	*GTK_RETLOC_BOOL(args[0]) = FALSE;
    else
	*GTK_RETLOC_BOOL(args[0]) = TRUE;
    Py_DECREF(ret);
    PyGTK_UNBLOCK_THREADS
}

/* callback for input handlers */
void
pygtk_input_marshal(gpointer a, PyObject *func, int nargs, GtkArg *args)
{
    PyObject *tuple, *ret;

    PyGTK_BLOCK_THREADS
    tuple = Py_BuildValue("(ii)", GTK_VALUE_INT(args[0]),
			  GTK_VALUE_FLAGS(args[1]));
    ret = PyObject_CallObject(func, tuple);
    Py_DECREF(tuple);
    if (ret == NULL) {
	if (PyGtk_FatalExceptions)
	    gtk_main_quit();
	else {
	    PyErr_Print();
	    PyErr_Clear();
	}
    } else
	Py_DECREF(ret);
    PyGTK_UNBLOCK_THREADS
}