I am trying to get a library (written by someone else) to run on
OpenSuse 12.1; I am not an X developer, but I inherited it and need
things to run. The library is tested and runs on FC13, RHEL 5 and SL6
without problems. But it consistently creates deadlocks in X calls on
OpenSuse 12.1
The sequence of events is
1. XInitThreads is called
2. The library creates a thread ("XEventThread") and starts executing
it. The thread executes a simple loop that repeatedly calls XNextEvent
and passes the returned events elsewhere.
3. XEventThread calls XNextEvent
4. XNextEvent blocks waiting for events; it does not return
immediately because there are no windows yet
5. XEventThread releases control to the main app thread
6. the main app thread calls XLockScreen
7 the main app thread starts creating windows
8. The first call to X functions, e.g. XCreateSimpleWindow, hangs
This is with XSyncrhonize enabled; if it is disabled, a few X calls
will succeed, but eventually something will hang forever.
On the OSes that work (FC13, ScientificLinux 6) the difference is
point 8 - the calls to X never lock, everything proceeds happily. It
works with a variety of window managers.
Why is there a difference on the OpenSuse, and what can I do about it?
I am happy to file a bug report with Novell, too, if this appears to
be a bug.
I originally asked a version of this question on the general X list
(well, it started less clearly defined), and got a suggestion that
this may be a thread safety fix missing from OpenSuse XLib.
http://lists.freedesktop.org/archives/xorg/2012-June/054726.html
I also created a cut-down version of the library (in the attached
file) that displays the behaviour. On FC13 and RHEL it creates 3
windows with 3-second intervals between them, and then waits forever.
It succeeds every time. On OpenSuse 12.1 it freezes as soon as the
thread starts, usually on the first call to XCreateSimpleWindow during
the creation of the first window. It is not affected by a window
manager - fails for all of them.
To compile
gcc -O2 -ansi -g -DXGRAFIX -I. -DUNIX -D_cplusplus -D_XOPEN_SOURCE=500
-D_REENTRANT -c -o HGraf.o HGraf.c
gcc HGraf.o -lpthread -lm -lX11 -L/usr/X11R6/lib
mv a.out HGrafTest
to run
./HGrafTest
Is this indeed an OpenSuse bug, or am I missing something about the program?
Thanks,
Myrosia
#include <pthread.h>
#include <stdio.h> /* Standard Libraries */
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <limits.h>
#include <errno.h>
#include <signal.h>
#include <assert.h>
#include <sys/timeb.h>
#include <unistd.h>
#include <X11/Xlib.h> /* the X11 stuff makes string.h also available */
#include <X11/Xutil.h>
#include <X11/Xos.h>
#define XK_MISCELLANY /* use miscellaneous keysym defs */
#include <X11/keysymdef.h>
#define MAX_GC 4 /* we need 1 GC for each transfer mode since changing
the transfer mode in the current GC is unreliable on
some X-servers that we have tested */
#define GSTP 4
#define DEF_FONTSIZE 16 /* default font size */
#define MAX_POINT 64 /* max number of points for polygons */
#define MAX_GREYS 64 /* implementations may quantise further */
#define MAX_COLOURS 16
static char *winClassName = "hgraf"; /* ... and its name */
static long int colours[MAX_COLOURS];
static long int greys[MAX_GREYS];
static int dispDEEP=0; /* display characteristics */
static int dispWIDE=0;
static int dispHIGH=0;
static char *FONTNAME = "Helvetica";
enum _HColour { WHITE, YELLOW, ORANGE, RED, MAUVE, PURPLE, DARK_BLUE,
LIGHT_BLUE, DARK_GREEN, LIGHT_GREEN, DARK_BROWN, LIGHT_BROWN,
LIGHT_GREY, GREY, DARK_GREY, BLACK};
typedef enum _HColour HColour; /* implementations may map these onto grey */
enum _XferMode {GCOPY, GOR, GXOR, GINVERT};
typedef enum _XferMode XferMode;
static char *XColArray[] = {
"white","yellow","orange","red",
"plum", "purple","blue","lightblue",
"darkgreen","palegreen", "brown","tan",
"lightgray", "gray","darkslategray", "black"
};
static int TFuncX11[4] = {
GXcopy, GXor, GXxor, GXinvert
};
void HandleExpose(XExposeEvent *xexpose);
void startXThread();
/* global things to put into window */
static Colormap theCmap;
static unsigned long black, white;
static Display *globDisp;
static long int eMask;
#define NO_OF_FONTS 9
#define FONTS_AVAILABLE 9
static char *FontNm[NO_OF_FONTS] = {
"-*-Medium-R-Normal-*-8-*-*-*-*-*-*-*",
"-*-Medium-R-Normal-*-10-*-*-*-*-*-*-*",
"-*-Medium-R-Normal-*-12-*-*-*-*-*-*-*",
"-*-Medium-R-Normal-*-14-*-*-*-*-*-*-*",
"-*-Medium-R-Normal-*-15-*-*-*-*-*-*-*",
"-*-Medium-R-Normal-*-16-*-*-*-*-*-*-*",
"-*-Medium-R-Normal-*-19-*-*-*-*-*-*-*",
"-*-Medium-R-Normal-*-20-*-*-*-*-*-*-*",
"-*-Medium-R-Normal-*-24-*-*-*-*-*-*-*" };
static char *FontNmBold[NO_OF_FONTS] = {
"-*-Bold-R-Normal-*-8-*-*-*-*-*-*-*",
"-*-Bold-R-Normal-*-10-*-*-*-*-*-*-*",
"-*-Bold-R-Normal-*-12-*-*-*-*-*-*-*",
"-*-Bold-R-Normal-*-14-*-*-*-*-*-*-*",
"-*-Bold-R-Normal-*-15-*-*-*-*-*-*-*",
"-*-Bold-R-Normal-*-16-*-*-*-*-*-*-*",
"-*-Bold-R-Normal-*-19-*-*-*-*-*-*-*",
"-*-Bold-R-Normal-*-20-*-*-*-*-*-*-*",
"-*-Bold-R-Normal-*-24-*-*-*-*-*-*-*" };
static int FontSize[NO_OF_FONTS] = {8, 10, 12, 14, 15, 16, 19, 20, 24};
static XFontStruct *DefaultFont, *CurrentFont, *FontInfo[NO_OF_FONTS], *FontInfoBold[NO_OF_FONTS];
#define FONT1 "-*-lucida-medium-r-*-*-12-*"
#define FONT2 "-*-helvetica-medium-r-*-*-12-*"
#define FONT3 "6x13"
/* -------------------------- Threads -------------------------------*/
typedef pthread_t HThreadT;
typedef struct { /* tracks nested calls */
pthread_mutex_t mutex; /* basic mutex */
pthread_t owner; /* owner of the lock */
int depth; /* number of nested "EnterSection" calls */
} HLockT;
typedef pthread_cond_t HSignalT;
#define TASKTYPE void *
#define TASKMOD
typedef enum { HPRIO_HIGH, HPRIO_NORM, HPRIO_LOW } HPriority;
typedef struct _QEntryRec *QEntry;
typedef struct _QEntryRec{
QEntry next;
void *hev;
} QEntryRec;
typedef struct {
QEntry head;
QEntry tail;
pthread_mutex_t mux;
pthread_cond_t cond;
} EventQueue;
static HLockT tlock; /* lock protecting this module */
static HLockT glock; /* global lightweight lock */
static EventQueue global_xeq;
/* HTError: report an error and die */
static void HTError(const char *msg, int errn)
{
fprintf(stderr,"*** Thread Error ***\n%s [%d] %s\n\n",msg,errn, strerror(errn));
exit(1);
}
/* HTCreate: create the internal lock and signal needed for monitoring */
static void HTCreate()
{
int n;
n = pthread_mutex_init(&tlock.mutex,NULL);
if(n!=0)
HTError("HTCreate: cannot create internal lock",n);
}
/* HGLockCreate: create the global lightweight lock */
static void HGLockCreate()
{
int n;
n = pthread_mutex_init(&glock.mutex,NULL);
if(n!=0)
HTError("HGLockCreate: cannot create internal lock",n);
}
/* InitThreads: initialise this module */
void InitThreads()
{
int rc;
HTCreate();
HGLockCreate();
XInitThreads();
rc = pthread_cond_init(&(global_xeq.cond),NULL);
if (rc!=0)
HTError("InitThreads: cant create signal",rc);
rc= pthread_mutex_init(&(global_xeq.mux),NULL);
if (rc!=0)
HTError("InitThreads: cant create mux",rc);
}
/* HTLock: obtain the internal lock */
static void HTLock(void)
{
int n,rc;
n = pthread_mutex_lock(&tlock.mutex);
if (n!=0)
HTError("HTLock: cannot set lock\n",n);
}
/* HTUnlock: release the internal lock */
static void HTUnlock(void)
{
int n;
n = pthread_mutex_unlock(&tlock.mutex);
if (n!=0)
HTError("HTUnlock: cannot release lock\n",n);
}
/* HCreateThread: Exec task(arg) as thread with prio p, store thread in *tp */
void HCreateThread(const char *name, int prBufLines, HPriority pr,
TASKTYPE (TASKMOD *task)(void *), void *arg){
static unsigned int threadIDCounter = 1;
int i;
unsigned int threadID;
int rc;
int scope;
pthread_mutexattr_t mattr;
pthread_attr_t attr;
struct sched_param param;
EventQueue xeq;
pthread_t thread;
HTLock();
pthread_attr_init(&attr);
rc = pthread_attr_setschedpolicy(&attr,SCHED_OTHER);
if (rc != 0) {
HTError("HCreateThread: Cannot set policy\n",rc);
}
switch(pr){
case HPRIO_HIGH:
param.sched_priority = sched_get_priority_max(SCHED_OTHER);
pthread_attr_setschedparam(&attr,¶m);
break;
case HPRIO_LOW:
param.sched_priority = sched_get_priority_min(SCHED_OTHER);
pthread_attr_setschedparam(&attr,¶m);
break;
case HPRIO_NORM:
break;
default:
HTError("HCreateThread: Bad priority\n" ,pr);
}
rc = pthread_create(&thread, &attr, task, arg);
if (rc != 0)
HTError("HCreateThread: Cannot create thread",rc);
threadID = ++threadIDCounter;
xeq.head=NULL;
xeq.tail=NULL;
rc = pthread_cond_init(&(xeq.cond),NULL);
if (rc!=0)
HTError("HCreateThread: cant create signal",rc);
rc=pthread_mutex_init(&(xeq.mux),NULL);
if (rc!=0)
HTError("HCreateThread: cant create mux",rc);
HTUnlock();
}
/* --------------------------- Initialisation ---------------------- */
static int nParm = 0;
static int trace = 0; /* Just for consistency */
/* LoadColours: create basic colour map */
void LoadColours(void)
{
int pixVal=0;
int c;
XColor colourDef;
for (c = 0; c < MAX_COLOURS; c++){
/* get colour from the X11 database */
if (!XParseColor(globDisp, theCmap, XColArray[c], &colourDef))
printf("InstallColours: Colour name %s not in X11 database",
XColArray[c]);
if (!XAllocColor(globDisp, theCmap, &colourDef))
printf("InstallColours: Cannot allocate colour %s", XColArray[c]);
else
pixVal = colourDef.pixel;
colours[c] = pixVal;
}
}
/* LoadGreys: create basic grey map */
void LoadGreys(void)
{
int c, f, ggap, steps[GSTP] = {8, 4, 2, 1};
XColor greyDef, whiteDef, blackDef, colourDef;
short RGBval, step;
int pixVal=0;
white = colours[0];
black = colours[15];
if (dispDEEP == 1){
/* map all grey levels onto b/w */
printf("single level display depth\n");
for (c = 0; c < MAX_GREYS/2; c++)
greys[c] = white;
for (c = MAX_GREYS/2; c < MAX_GREYS; c++)
greys[c] = black;
} else {
/* then the grey levels */
whiteDef.pixel = white; XQueryColor(globDisp, theCmap, &whiteDef);
blackDef.pixel = black; XQueryColor(globDisp, theCmap, &blackDef);
ggap = ((int)(whiteDef.red - blackDef.red))/MAX_GREYS;
for (f = 0; f < GSTP; f++){
step = steps[f]*ggap;
for (c = 0; c < (MAX_GREYS/steps[f]); c++){
RGBval = blackDef.red + c*step;
greyDef.red = RGBval;
greyDef.green = RGBval;
greyDef.blue = RGBval;
if (!XAllocColor(globDisp, theCmap, &greyDef))
printf("InstallColours: Cannot allocate grey level %d",
c*steps[f]);
else
pixVal = greyDef.pixel;
greys[c] = pixVal;
}
}
}
}
/*------------------- Font Handling Routines -----------------------*/
/* InstallFonts: install the HGraf font set */
static void InstallFonts(void)
{
int i;
int nfonts, j;
char **pfinfo;
/* pfinfo=(char**)New(&gcheap,sizeof(char)*100*100);*/
/* load user fonts */
for (i=0; i < FONTS_AVAILABLE; i++) {
if ((FontInfo[i] = XLoadQueryFont(globDisp, FontNm[i])) == NULL) {
pfinfo=XListFonts(globDisp, FontNm[i], 100, &nfonts);
j=0;
printf("%d matching fonts \n", nfonts);
if(nfonts>0)
while(((FontInfo[i] = XLoadQueryFont(globDisp, pfinfo[j])) == NULL) && j<nfonts) {
j++;
}
if(j==nfonts||nfonts==0)
printf("InstallFonts: Cannot load font %s", FontNm[i]);
XFreeFontNames(pfinfo);
}
if ((FontInfoBold[i] = XLoadQueryFont(globDisp, FontNmBold[i])) == NULL) {
pfinfo=XListFonts(globDisp, FontNmBold[i], 100, &nfonts);
j=0;
printf("%d matching fonts \n", nfonts);
if(nfonts>0)
while(((FontInfo[i] = XLoadQueryFont(globDisp, pfinfo[j])) == NULL) && j<nfonts) {
j++;
}
if(j==nfonts||nfonts==0)
printf("InstallFonts: Cannot load font %s", FontNmBold[i]);
XFreeFontNames(pfinfo);
}
}
/* load the default font */
if ((DefaultFont = XLoadQueryFont(globDisp, FONT1))==NULL &&
(DefaultFont = XLoadQueryFont(globDisp, FONT2))==NULL &&
(DefaultFont = XLoadQueryFont(globDisp, FONT3))==NULL)
printf("InstallFonts: Cannot load default font");
}
/* EXPORT->InitGraf: initialise memory and configuration parameters */
void InitGraf()
{
int i;
int screen;
globDisp=XOpenDisplay(0); /*global display connection */
if(globDisp==NULL)
printf("Error opening display\n");
screen=DefaultScreen(globDisp);
InstallFonts();
theCmap = DefaultColormap(globDisp, screen);
dispWIDE = DisplayWidth(globDisp, screen);
dispHIGH = DisplayHeight(globDisp, screen);
dispDEEP = DisplayPlanes(globDisp, screen);
white = WhitePixel(globDisp,screen);
black = BlackPixel(globDisp,screen);
eMask = ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask |
ButtonReleaseMask | PointerMotionHintMask| PointerMotionMask;
LoadColours(); LoadGreys();
XSynchronize(globDisp, 1);
startXThread();
}
/* ----------------------------- Event Handling ------------------------------ */
TASKTYPE TASKMOD XEventThread(void *data)
{
XEvent xev;
do{
printf("About to call getNextEvent on XEventThread\n", xev.type);
fflush(stdout);
XNextEvent(globDisp, &xev);
printf("Event received on an event thread %i\n", xev.type);
fflush(stdout);
}
while(1);
}
void startXThread()
{
HCreateThread("XEventThread",10,HPRIO_NORM,XEventThread,(void *)0);
}
/* EXPORT->HFlush flush any pending draw operations */
void HFlush()
{
XFlush(globDisp);
}
/* EXPORT-> HSetColour: Set current colour to c */
void HSetColour(Display *theDisp, HColour c, GC* gcs)
{
XferMode xf;
for (xf = GCOPY; xf < GINVERT; xf=(XferMode) (xf+1)) /* change all GCs except GINVERT*/
XSetForeground(theDisp, gcs[(int) xf], colours[(int) c]);
}
/* -------------------- Window Creation/Destruction --------------------- */
void InitGCs(Display* theDisp, Window theWindow, GC* gcs)
{
XferMode xf;
XGCValues values;
unsigned long mask;
mask = GCLineWidth | GCFunction | GCForeground;
for (xf = GCOPY; xf < GINVERT; xf=(XferMode) (xf+1)){
values.line_width = 0;
values.function = TFuncX11[(int) xf];
values.foreground = black;
gcs[(int) xf] = XCreateGC(theDisp, theWindow, mask, &values);
XSetGraphicsExposures(theDisp,gcs[(int) xf], False);
}
mask = GCLineWidth | GCFunction | GCPlaneMask | GCForeground;
values.line_width = 0;
values.function = GXxor;
values.foreground = ~0;
values.plane_mask = black ^ white;
gcs[(int) GINVERT] = XCreateGC(theDisp, theWindow, mask, &values);
}
/* EXPORT-> MakeHWin: Create and open window, initialization */
void MakeHWin(char *wname, int x, int y, int w, int h, int bw)
{
char sbuf[256], *hgraf = "HGraf";
Display *theDisp;
Window rootW, theWindow;
XSetWindowAttributes setwinattr;
unsigned long vmask;
Atom del_atom;
int theScreen;
Pixmap thePixmap;
int hintsFlags;
XSizeHints hints;
GC gcs[MAX_GC];
theDisp=globDisp;
XLockDisplay(theDisp);
printf("About to create a window in makeHWin\n");
fflush(stdout);
theScreen = DefaultScreen(theDisp);
rootW=RootWindow(theDisp, theScreen);
printf("Calling XCreateSimpleWindow in makeHWin\n");
fflush(stdout);
theWindow = XCreateSimpleWindow(theDisp, rootW, x, y, w, h, bw, black, white );
/* allow for backing up the contents of the window */
vmask = CWBackingStore; setwinattr.backing_store = WhenMapped;
printf("Calling XCreateWindowAttributes in makeHWin\n");
fflush(stdout);
XChangeWindowAttributes(theDisp, theWindow, vmask, &setwinattr);
printf("Calling XCreatePixmap in makeHWin\n");
fflush(stdout);
/* create a pixmap for backing store */
thePixmap=XCreatePixmap(theDisp, theWindow, w, h, dispDEEP);
/* set the size hints for the window manager */
hints.flags = PPosition | PSize | PMaxSize | PMinSize;
hints.y = y; hints.x = x;
hints.width = w; hints.height = h;
hints.min_width = w; hints.min_height = h;
hints.max_width = w; hints.max_height = h;
/* compose the name of the window */
printf("Calling XSetStandardProperties in makeHWin\n");
fflush(stdout);
XSetStandardProperties(theDisp, theWindow, wname, hgraf, None, NULL, 0, &(hints));
/* select events to receive */
printf("Calling XSelectInput in makeHWin\n");
fflush(stdout);
XSelectInput(theDisp, theWindow, eMask);
/* explicitly request to get toplevel window kill events */
printf("Calling XInternAtom/XSetWMProtocols in makeHWin\n");
fflush(stdout);
if ((del_atom = XInternAtom(theDisp, "WM_DELETE_WINDOW",True)) != None)
XSetWMProtocols(theDisp, theWindow, &del_atom, 1);
printf("Calling XMapWindow in makeHWin\n");
fflush(stdout);
XMapWindow(theDisp, theWindow);
printf("Calling InitGCs in makeHWin\n");
fflush(stdout);
InitGCs(theDisp, theWindow, gcs);
printf("Calling XFillRectangle makeHWin\n");
fflush(stdout);
XFillRectangle(theDisp, thePixmap, gcs[0], 0,0, w,h);
printf("Calling HSetColour makeHWin\n");
fflush(stdout);
HSetColour(theDisp,WHITE,gcs);
printf("done makeHWin\n");
fflush(stdout);
XUnlockDisplay(theDisp);
}
int main(int argc, char *argv[])
{
InitThreads();
InitGraf();
printf("Making window 1 \n");
MakeHWin("w1",40,40,200,50,0);
sleep(10);
printf("Making window 2 \n");
MakeHWin("w2",140,140,200,50,1);
sleep(10);
printf("Making window 3 \n");
MakeHWin("w3",240,240,200,50,2);
sleep(10);
printf("Waiting forever\n");
while(1) {
}
}