Hanging of XShmPutImage event notification
Asked Answered
A

2

13

I am using XShm extension to draw and manipulate images in Linux.

In order to not have screen flickering, I am passing send_event = TRUE to XShmPutImage and then waiting for the event with XIfEvent, immediately after the call to XScmPutImage.

This way, I am making the image drawing blocking in order to not changing the image until it is displayed on the window surface.

Usually everything works fine. But sometimes, when I have intensive image drawing, it seems that the event never comes and the drawing procedure hangs.

Where to see for the problem? Is using XIfEvent appropriate for this task? How can the event dissapear from the message queue?

Is it possible XShmPutImage to not send event (if send_event = TRUE) or to send event different than ShmCompletion on some circumstances? (for example on some internal error or something?)

EDIT:

After some more research, I found that such hangs happens only when the window manager intensively generate events to the window. For example when I resize the window by dragging its corners.

EDIT2:

I tried several ways to solve this problem, but without success. At the end I was forced to use some timeout and to cancel waiting after some time. But of course this is dirty hack and I want to fix it anyway.

So, what can be the reason XShmPutImage to not send event if send_event=TRUE or is it possible this event to disappear from the message queue?

EDIT3:

Here is the questionable code (FASM):

        cinvoke XShmPutImage, ......, TRUE

    .loop:
        lea     eax, [.event]
        cinvoke XCheckTypedEvent, [Display], [ShmCompletionEvent], eax

        test    eax, eax
        jz      .loop      ; there is no message

NB: XShmPutImage always return TRUE, regardless whether the event check hangs or not, so I didn't put error check after it.

EDIT4:

Because of request I am posting the whole code of the drawing function. The code uses some macro libraries of FASM, but at least the ideas are clear (I hope)

Notice that this code contains workaround code that limits the event waiting for only 20ms. Without this timeout the waiting loop simply hangs forever. The number of the XShm event is acquired by calling XShmGetEventBase as recommended in the Xshm documentation.

; Draws the image on a OS provided window surface.
proc DrawImageRect, .where, .pImage, .xDst, .yDst, .xSrc, .ySrc, .width, .height
.event XEvent
       rb 256
begin
        pushad

        mov     esi, [.pImage]
        test    esi, esi
        jz      .exit

        mov     ebx, [esi+TImage.ximage]

        cinvoke XCreateGC, [hApplicationDisplay], [.where], 0, 0
        mov     edi, eax


        cinvoke XShmPutImage, [hApplicationDisplay], [.where], edi, [esi+TImage.ximage], [.xSrc], [.ySrc], [.xDst], [.yDst], [.width], [.height], TRUE

        stdcall GetTimestamp
        lea     esi, [eax+20]    ; 20ms timeout

.loop:
        lea     eax, [.event]
        cinvoke XCheckTypedEvent, [hApplicationDisplay], [ShmCompletionEvent], eax
        test    eax, eax
        jnz     .finish

        stdcall GetTimestamp
        cmp     eax, esi
        jb      .loop

.finish:
        cinvoke XFreeGC, [hApplicationDisplay], edi

.exit:
        popad
        return

endp

And here is the code of the main event loop of the application.

The procedure __ProcessOneSystemEvent simply dispatches the events to the GUI objects and ignores all events it does not use. It does not process ShmCompletionEvent at all.

All the windows created in the application have events mask of: ExposureMask+FocusChangeMask+KeyPressMask+KeyReleaseMask+ButtonPressMask+ButtonReleaseMask+EnterWindowMask+LeaveWindowMask+PointerMotionMask+StructureNotifyMask

proc ProcessSystemEvents
  .event  XEvent
          rb 256
begin
        push    ebx ecx edx

.event_loop:
; check for quit

        get     eax, [pApplication], TApplication:MainWindow

        test    eax, eax
        jz      .continue    

        cmp     dword [eax], 0
        jne     .continue

        cinvoke XFlush, [hApplicationDisplay]
        xor     eax, eax
        mov     [fGlobalTerminate], 1
        stc
        pop     edx ecx ebx
        return

.continue:
        cinvoke XPending, [hApplicationDisplay]
        test    eax, eax
        jz      .noevents

        push    edi ecx
        lea     edi, [.event]
        mov     ecx, sizeof.XEvent/4
        xor     eax, eax
        rep stosd
        pop     ecx edi

        lea     ebx, [.event]

        cinvoke  XNextEvent, [hApplicationDisplay], ebx
        stdcall  __ProcessOneSystemEvent, ebx
        jmp      .event_loop

.noevents:
        clc
        pop     edx ecx ebx
        return

endp

The full source code is available in the repository but it is a very big project not easy for navigation. The discussed source is in check-in 8453c99b1283def8.

The files: "freshlib/graphics/images.asm" and "freshlib/graphics/Linux/images.asm" are about the image drawing.

The files: "freshlib/gui/Main.asm" and "freshlib/gui/Linux/Main.asm" are about the general events handling in the application.

Alanealanine answered 15/11, 2015 at 14:49 Comment(8)
This is not exactly a clear question.There is a number of possible reasons including but not limited to undefined behavior. Also, is it c or c++?Unspeakable
@iharob Well, the actual code is written in assembly language (FASM). But IMHO, the C/C++ programmers should be more competent on XLib, XShm and Linux programming. If you write what is not clear, I can edit the question in order to make it better.Alanealanine
Could you please post your XShmPutImage code along with your code related to Shm setup and XEvent processing. I've looked at the X server source code and I have an idea, but the information could help me post the answer.Coarctate
@CraigEstey Here it is in the question, edit4Alanealanine
I believe I'm getting closer. Could I trouble you [once again] for some more code? The code that calls DrawImageRect, the code that sets up the shm area for .pImage, and the event handling code for .where [what your event dispatch function actually calls for this drawable--if any]Coarctate
Scratch that request, as I just noticed your link to your repository--Thanks, I'll probably post an answer soon.Coarctate
@CraigEstey - I will answer anyway. Generally DrawImageRect is called only as a response to expose events received by the application. The shm area is set up when the image is created: here. The drawable is always the window surface. The windows are one-level only, there is no children windows.Alanealanine
Thanks, that helped. I noodled around and found [and downloaded] a tarball for commit c5c765bc7e. Found what I believe is the "smoking gun". I knew the what and now I think I know the why. Confirms what I had been suspecting. I'll write it up and post an answer soon, probably within a few hours--I want to double check a bit further.Coarctate
C
4

What is the X server doing?

The X server can and will suppress a ShmCompletionEvent if the parameters passed to XShmPutImage exceed the geometry of the shared memory area attached to the XImage in the call. The server checks X/Y and width/height against the previously stored limits for the given shared area and if the call parameters are out-of-bounds, the server will return BadValue, suppress the drawing operation, and suppress the completion event.

The above is exactly what is happening in your library. Here's how:

  1. The main event dispatcher routine is ProcessSystemEvents. It performs an XEventNext, and based on event type, using a jump table .jump_table dispatches to an event specific handler function.
  2. The event specific function for an Expose event is .expose
  3. The .expose function will, in turn, call DrawImageRect using X/Y and width/height values from the XExposeEvent struct. This is wrong and is the true source of the bug, as we shall see momentarily.
  4. DrawImageRect will pass these values along in a call to XShmPutImage
  5. The handler for XShmPutImage in the X server will examine these parameters and reject if they're out of bounds.

The parameters are being rejected because they come from an exposure event and are related to the geometry of the window and not the geometry of the shared memory attached to the XImage used in the XShmPutImage call.

Specifically, if the window has just be resized (e.g. by the window manager) and has been enlarged, and there has been a prior ConfigureNotify event for resize. Now, with a new Expose event, it will have larger width/height that will exceed the width/height of the shared memory area that the server knows about.

It is the responsibility of the client to field the window resize events [etc.] and teardown/recreate the shared memory area with the enlarged size. This is not being done and is the source of the bug.

NOTE: Just to be completely clear on this, the server can only report on the error and not do anything about it for a few reasons:

  1. The server knows about the window [and its resize].
  2. It knows about the XImage, its shared memory area and size
  3. But they're only associated during the XShmPutImage call [AFAIK]
  4. Even if the server could associate them, it couldn't adjust the shmarea
  5. That's because it has no way to relink the shmarea to the client side
  6. Only the client can do that via XShmDetach/XShmAttach

Below are redacted versions of the relevant source files from the c5c765bc7e commit. They have been cleaned up a bit, so only the most germane parts remain. Some lines have been truncated or wrapped to eliminate horizontal scrolling.

The files have been annotated with NOTE and NOTE/BUG which I did while analyzing them.


gui/Main.asm The top level generic main loop. Nothing to see much to see here.

; FILE: gui/Main.asm
; _____________________________________________________________________________
;|                                                                             |
;| ..::FreshLib::.. Free, open source. Licensed under "BSD 2-clause" license." |
;|_____________________________________________________________________________|
;
;  Description: Main procedure of GUI application library.
;
;  Target OS: Any
;
;  Dependencies:
;
;  Notes: Organize the main message/event loop needed by every GUI engine.
;         This file contains only OS independent part and includes OS dependent
;         files.
;______________________________________________________________________________

module "Main library"

proc Run
begin
.mainloop:
        stdcall ProcessSystemEvents
        jc      .terminate

        mov     eax, [pApplication]
        test    eax, eax
        jz      .eventok

        get     ecx, eax, TApplication:OnIdle
        jecxz   .eventok

        stdcall ecx, eax

.eventok:
        stdcall WaitForSystemEvent
        jmp     .mainloop

.terminate:
        DebugMsg "Terminate GUI application!"
        return
endp

include '%TargetOS%/Main.asm'

endmodule

gui/Linux/Main.asm The event handlers

; FILE: gui/Linux/Main.asm
; _____________________________________________________________________________
;|                                                                             |
;| ..::FreshLib::.. Free, open source. Licensed under "BSD 2-clause" license." |
;|_____________________________________________________________________________|
;
;  Description: Main procedure of GUI application library.
;
;  Target OS: Linux
;
;  Dependencies:
;
;  Notes: Organize the main message/event loop needed by every GUI engine.
;______________________________________________________________________________

body ProcessSystemEvents
; NOTE: this is the storage for the dequeued event -- all dispatch routines
; should use it and process it
  .event  XEvent
          rb 256

begin
        push    ebx ecx edx

.event_loop:
; check for quit

        get     eax, [pApplication], TApplication:MainWindow

        test    eax, eax
        jz      .continue     ; ???????????

        cmp     dword [eax], 0
        jne     .continue

        cinvoke XFlush, [hApplicationDisplay]
        xor     eax, eax
        mov     [fGlobalTerminate], 1
        stc
        pop     edx ecx ebx
        return

; NOTE: it is wasteful for the main loop to call WaitForSystemEvent, then call
; us and we do XPending on the first loop -- we already know we have at least
; one event waiting in the queue
.continue:
        cinvoke XPending, [hApplicationDisplay]
        test    eax, eax
        jz      .noevents

        push    edi ecx
        lea     edi, [.event]
        mov     ecx, sizeof.XEvent/4
        xor     eax, eax
        rep stosd
        pop     ecx edi

        lea     ebx, [.event]

        cinvoke  XNextEvent, [hApplicationDisplay], ebx
        stdcall  __ProcessOneSystemEvent, ebx
        jmp      .event_loop

.noevents:
        clc
        pop     edx ecx ebx
        return

endp

body WaitForSystemEvent
.event XEvent
begin
        push    eax ecx edx
        lea     eax, [.event]
        cinvoke XPeekEvent, [hApplicationDisplay], eax
        pop     edx ecx eax
        return
endp

proc __ProcessOneSystemEvent, .linux_event
begin
        pushad

        mov     ebx, [.linux_event]

;        mov     eax, [ebx+XEvent.type]
;        cmp     eax, [ShmCompletionEvent]
;        je      .shm_completion

        stdcall _GetWindowStruct, [ebx+XEvent.window]
        jc      .notprocessed

        test    eax, eax
        jz      .notprocessed

        mov     esi, eax
        mov     ecx, [ebx+XEvent.type]

        cmp     ecx, LASTEvent
        jae     .notprocessed

        mov     ecx, [.jump_table+4*ecx]
        jecxz   .notprocessed

        jmp     ecx

.notprocessed:
        popad
        stc
        return

.finish:
        popad
        clc
        return

;.shm_completion:
;        DebugMsg "Put back completion event!"
;
;        int3
;        cinvoke XPutBackEvent, [hApplicationDisplay], ebx
;        jmp     .finish

;.........................................................................
; seMove and seResize events.
;-------------------------------------------------------------------------
.moveresize:

; NOTE/BUG!!!!: we must not only process a resize/move request, but we must also
; adjust the size of the shmarea attached to the XImage -- that is _not_ being
; done. (e.g.) if the window is enlarged, the shmarea must be enlarged

        cinvoke XCheckTypedWindowEvent, [hApplicationDisplay],
                [ebx+XConfigureEvent.window], ConfigureNotify, ebx
        test    eax, eax
        jnz     .moveresize

; resize event...
        mov     eax, [esi+TWindow._width]
        mov     edx, [esi+TWindow._height]
        cmp     eax, [ebx+XConfigureEvent.width]
        jne     .resize
        cmp     edx, [ebx+XConfigureEvent.height]
        je      .is_move

.resize:
        exec    esi, TWindow:EventResize, [ebx+XConfigureEvent.width],
                [ebx+XConfigureEvent.height]

; move event...
.is_move:
        mov     eax, [esi+TWindow._x]
        mov     edx, [esi+TWindow._y]
        cmp     eax, [ebx+XConfigureEvent.x]
        jne     .move
        cmp     eax, [ebx+XConfigureEvent.y]
        je      .finish

.move:
        exec    esi, TWindow:EventMove,
                [ebx+XConfigureEvent.x], [ebx+XConfigureEvent.y]

        jmp     .finish

;.........................................................................
; DestroyNotify handler it invalidates the handle in TWindow structure and
; then destroys TWindow.
.destroy:
        test    esi, esi
        jz      .finish

        mov     [esi+TWindow.handle], 0

        destroy esi
        jmp     .finish

;.........................................................................
; Window paint event

.expose:
        get     edi, esi, TWindow:ImgScreen

; NOTE:BUG!!!!!
;
; if the window has been resized (e.g. enlarged), these values are wrong!
; they relate to the _window_ but _not_ the shmarea that is attached to the
; XImage
;
; however, DrawImageRect will call XShmPutImage with these values, they
; will exceed the geometry of what the X server knows about the shmarea and
; it will return BadValue and _suppress_ the completion event for XShmPutImage

        stdcall DrawImageRect, [esi+TWindow.handle], edi,
                [ebx+XExposeEvent.x],[ebx+XExposeEvent.y],
                [ebx+XExposeEvent.x], [ebx+XExposeEvent.y],
                [ebx+XExposeEvent.width], [ebx+XExposeEvent.height]

        jmp     .finish

;.........................................................................
; Mouse event handlers

.mousemove:

        cinvoke XCheckTypedWindowEvent, [hApplicationDisplay],
                [ebx+XConfigureEvent.window], MotionNotify, ebx
        test    eax, eax
        jnz     .mousemove

        stdcall ServeMenuMouseMove, [ebx+XMotionEvent.window],
                [ebx+XMotionEvent.x], [ebx+XMotionEvent.y],
                [ebx+XMotionEvent.state]
        jc      .finish

        cinvoke XCheckTypedWindowEvent, [hApplicationDisplay],
                [ebx+XMotionEvent.window], MotionNotify, ebx
        test    eax, eax
        jnz     .mousemove

        mov     edi, [__MouseTarget]
        test    edi, edi
        jz      .search_target_move

        stdcall __GetRelativeXY, edi, [ebx+XMotionEvent.x], [ebx+XMotionEvent.y]
        jmp     .target_move

.search_target_move:
        exec    esi, TWindow:ChildByXY, [ebx+XMotionEvent.x],
                [ebx+XMotionEvent.y], TRUE
        mov     edi, eax

.target_move:
        cmp     edi, [__LastPointedWindow]
        je      .move_event

        cmp     [__LastPointedWindow], 0
        je      .leave_ok

        exec    [__LastPointedWindow], TWindow:EventMouseLeave

.leave_ok:

        mov     [__LastPointedWindow], edi
        exec    edi, TWindow:EventMouseEnter

.move_event:
        exec    edi, TWindow:EventMouseMove, ecx, edx, [ebx+XMotionEvent.state]
        jmp     .finish

;.........................................................................
; event jump table
.jump_table dd  0                       ; event 0
            dd  0                       ; event 1
            dd  .key_press              ; KeyPress = 2
            dd  .key_release            ; KeyRelease = 3
            dd  .mouse_btn_press        ; ButtonPress = 4
            dd  .mouse_btn_release      ; ButtonRelease = 5
            dd  .mousemove              ; MotionNotify = 6
            dd  0                       ; EnterNotify = 7
            dd  0                       ; LeaveNotify = 8
            dd  .focusin                ; FocusIn = 9
            dd  .focusout               ; FocusOut = 10
            dd  0                       ; KeymapNotify = 11
            dd  .expose                 ; Expose = 12
            dd  0                       ; GraphicsExpose = 13
            dd  0                       ; NoExpose = 14
            dd  0                       ; VisibilityNotify = 15
            dd  0                       ; CreateNotify = 16
            dd  .destroy                ; DestroyNotify = 17
            dd  0                       ; UnmapNotify = 18
            dd  0                       ; MapNotify = 19
            dd  0                       ; MapRequest = 20
            dd  0                       ; ReparentNotify = 21
            dd  .moveresize             ; ConfigureNotify = 22
            dd  0                       ; ConfigureRequest = 23
            dd  0                       ; GravityNotify = 24
            dd  0                       ; ResizeRequest = 25
            dd  0                       ; CirculateNotify = 26
            dd  0                       ; CirculateRequest = 27
            dd  0                       ; PropertyNotify = 28
            dd  0                       ; SelectionClear = 29
            dd  0                       ; SelectionRequest = 30
            dd  0                       ; SelectionNotify = 31
            dd  0                       ; ColormapNotify = 32
            dd  .clientmessage          ; ClientMessage = 33
            dd  .mapping_notify         ; MappingNotify = 34

graphics/Linux/images.asm The image drawing code [including the DrawImageRect function] and the shared memory create/destroy code.

; FILE: graphics/Linux/images.asm
; _____________________________________________________________________________
;|                                                                             |
;| ..::FreshLib::.. Free, open source. Licensed under "BSD 2-clause" license." |
;|_____________________________________________________________________________|
;
;  Description: Memory based images manipulation library.
;
;  Target OS: Linux
;
;  Dependencies: memory.asm
;
;  Notes:
;______________________________________________________________________________

uses libX11, xshm

struct TImage
  .width   dd ?  ; width in pixels.
  .height  dd ?  ; height in pixels.
  .pPixels dd ?  ; pointer to the pixel memory.

; os dependent data
  .ximage  dd ?
  .shminfo XShmSegmentInfo
ends

body CreateImage
begin
        pushad

        stdcall GetMem, sizeof.TImage
        jc      .finish
        mov     esi, eax

        xor     eax, eax
        inc     eax

        mov     ecx, [.width]
        mov     edx, [.height]

        cmp     ecx, 0
        cmovle  ecx, eax

        cmp     edx, 0
        cmovle  edx, eax

        mov     [esi+TImage.width], ecx
        mov     [esi+TImage.height], edx

        lea     eax, [4*ecx]
        imul    eax, edx

        cinvoke shmget, IPC_PRIVATE, eax, IPC_CREAT or 777o
        test    eax, eax
        js      .error

        mov     [esi+TImage.shminfo.ShmID], eax

        cinvoke shmat, eax, 0, 0
        cmp     eax, -1
        je      .error_free

        mov     [esi+TImage.shminfo.Addr], eax
        mov     [esi+TImage.pPixels], eax
        mov     [esi+TImage.shminfo.fReadOnly], 1

        lea     ebx, [esi+TImage.shminfo]
        cinvoke XShmCreateImage, [hApplicationDisplay], 0, $20, ZPixmap, eax,
                ebx, [esi+TImage.width], [esi+TImage.height]
        mov     [esi+TImage.ximage], eax

        cinvoke XShmAttach, [hApplicationDisplay], ebx

        clc
        mov     [esp+4*regEAX], esi

.finish:
        popad
        return

.error_free:
        cinvoke shmctl, [ebx+XShmSegmentInfo.ShmID], IPC_RMID, 0

.error:
        stdcall FreeMem, esi
        stc
        jmp     .finish

endp

body DestroyImage
begin
        pushad

        mov     esi, [.ptrImage]
        test    esi, esi
        jz      .finish

        lea     eax, [esi+TImage.shminfo]
        cinvoke XShmDetach, [hApplicationDisplay], eax

        cinvoke XDestroyImage, [esi+TImage.ximage]

        cinvoke shmdt, [esi+TImage.shminfo.Addr]
        cinvoke shmctl, [esi+TImage.shminfo.ShmID], IPC_RMID, 0
        stdcall FreeMem, esi

.finish:
        popad
        return
endp

;if used ___CheckCompletionEvent
;___CheckCompletionEvent:
;
;virtual at esp+4
;  .display dd ?
;  .pEvent  dd ?
;  .user    dd ?
;end virtual
;
;; timeout
;        stdcall GetTimestamp
;        cmp     eax, [.user]
;        jbe     @f
;
;        DebugMsg "Timeout!"
;
;        mov     eax, 1
;        retn
;
;@@:
;        mov     eax, [.pEvent]      ;.pEvent
;        mov     eax, [eax+XEvent.type]
;
;        cmp     eax, [ShmCompletionEvent]
;        sete    al
;        movzx   eax, al
;        retn
;end if

body DrawImageRect
.event XEvent
       rb 256
begin
        pushad

        mov     esi, [.pImage]
        test    esi, esi
        jz      .exit

        mov     ebx, [esi+TImage.ximage]

; NOTE: is this necessary? it seems wasteful to create and destroy a GC
; repeatedly.  Dunno, does this _have_ to be done here, _every_ time?
        cinvoke XCreateGC, [hApplicationDisplay], [.where], 0, 0
        mov     edi, eax

; NOTE/BUG: The return ShmCompletionEvent will be suppressed due to a BadValue
; if the X/Y and width/height parameters given to us by caller exceed the
; geometry/range of the shmarea attached to .ximage
;
; the routine that calls us is .expose and it _is_ giving us bad values.  it is
; passing us X/Y width/height related to an exposure event of the .where
; _window_ which we put in the call.  The X server will compare these against
; the size of the shmarea of TImage.xmage and complain if we exceed the bounds

        cinvoke XShmPutImage, [hApplicationDisplay], [.where], edi,
                [esi+TImage.ximage], [.xSrc], [.ySrc], [.xDst], [.yDst],
                [.width], [.height], TRUE

; NOTE/BUG: this code should _not_ be looping on XCheckTypedEvent because it
; disrupts the normal event processing.  if we want to be "synchronous" on this
; we should loop on the main event dispatcher (ProcessSystemEvents) and let it
; dispatch to a callback we create.  we can set a "pending" flag that our [not
; yet existent] dispatch routine can clear

; THIS CODE SOMETIMES CAUSES HANGS!

        stdcall GetTimestamp
        lea     esi, [eax+20]

.loop:
        lea     eax, [.event]
        cinvoke XCheckTypedEvent, [hApplicationDisplay], [ShmCompletionEvent],
                eax
        test    eax, eax
        jnz     .finish

        stdcall GetTimestamp
        cmp     eax, esi
        jb      .loop

.finish:
        cinvoke XFreeGC, [hApplicationDisplay], edi

.exit:
        popad
        return

endp

Xext/shm.c The X server code that checks and processes the XShmPutImage call.

// FILE: Xext/shm.c

static int
ProcShmPutImage(ClientPtr client)
{
    GCPtr pGC;
    DrawablePtr pDraw;
    long length;
    ShmDescPtr shmdesc;

    REQUEST(xShmPutImageReq);

    REQUEST_SIZE_MATCH(xShmPutImageReq);
    VALIDATE_DRAWABLE_AND_GC(stuff->drawable, pDraw, DixWriteAccess);
    VERIFY_SHMPTR(stuff->shmseg, stuff->offset, FALSE, shmdesc, client);

    // NOTE: value must be _exactly_ 0/1
    if ((stuff->sendEvent != xTrue) && (stuff->sendEvent != xFalse))
        return BadValue;

    if (stuff->format == XYBitmap) {
        if (stuff->depth != 1)
            return BadMatch;
        length = PixmapBytePad(stuff->totalWidth, 1);
    }
    else if (stuff->format == XYPixmap) {
        if (pDraw->depth != stuff->depth)
            return BadMatch;
        length = PixmapBytePad(stuff->totalWidth, 1);
        length *= stuff->depth;
    }
    else if (stuff->format == ZPixmap) {
        if (pDraw->depth != stuff->depth)
            return BadMatch;
        length = PixmapBytePad(stuff->totalWidth, stuff->depth);
    }
    else {
        client->errorValue = stuff->format;
        return BadValue;
    }

    // NOTE/BUG: The following block is the "check parameters" code.  If the
    // given drawing parameters of the request (e.g. X, Y, width, height) [or
    // combinations thereof] exceed the geometry/size of the shmarea, the
    // BadValue error is being returned here and the code to send a return
    // event will _not_ be executed.  The bug isn't really here, it's on the
    // client side, but it's the client side bug that causes the event to be
    // suppressed

    /*
     * There's a potential integer overflow in this check:
     * VERIFY_SHMSIZE(shmdesc, stuff->offset, length * stuff->totalHeight,
     *                client);
     * the version below ought to avoid it
     */
    if (stuff->totalHeight != 0 &&
        length > (shmdesc->size - stuff->offset) / stuff->totalHeight) {
        client->errorValue = stuff->totalWidth;
        return BadValue;
    }
    if (stuff->srcX > stuff->totalWidth) {
        client->errorValue = stuff->srcX;
        return BadValue;
    }
    if (stuff->srcY > stuff->totalHeight) {
        client->errorValue = stuff->srcY;
        return BadValue;
    }
    if ((stuff->srcX + stuff->srcWidth) > stuff->totalWidth) {
        client->errorValue = stuff->srcWidth;
        return BadValue;
    }
    if ((stuff->srcY + stuff->srcHeight) > stuff->totalHeight) {
        client->errorValue = stuff->srcHeight;
        return BadValue;
    }

    // NOTE: this is where the drawing takes place
    if ((((stuff->format == ZPixmap) && (stuff->srcX == 0)) ||
         ((stuff->format != ZPixmap) &&
          (stuff->srcX < screenInfo.bitmapScanlinePad) &&
          ((stuff->format == XYBitmap) ||
           ((stuff->srcY == 0) &&
            (stuff->srcHeight == stuff->totalHeight))))) &&
        ((stuff->srcX + stuff->srcWidth) == stuff->totalWidth))
        (*pGC->ops->PutImage) (pDraw, pGC, stuff->depth,
                               stuff->dstX, stuff->dstY,
                               stuff->totalWidth, stuff->srcHeight,
                               stuff->srcX, stuff->format,
                               shmdesc->addr + stuff->offset +
                               (stuff->srcY * length));
    else
        doShmPutImage(pDraw, pGC, stuff->depth, stuff->format,
                      stuff->totalWidth, stuff->totalHeight,
                      stuff->srcX, stuff->srcY,
                      stuff->srcWidth, stuff->srcHeight,
                      stuff->dstX, stuff->dstY, shmdesc->addr + stuff->offset);

    // NOTE: this is where the return event gets sent
    if (stuff->sendEvent) {
        xShmCompletionEvent ev = {
            .type = ShmCompletionCode,
            .drawable = stuff->drawable,
            .minorEvent = X_ShmPutImage,
            .majorEvent = ShmReqCode,
            .shmseg = stuff->shmseg,
            .offset = stuff->offset
        };
        WriteEventsToClient(client, 1, (xEvent *) &ev);
    }

    return Success;
}
Coarctate answered 28/11, 2015 at 12:38 Comment(12)
Great! This is one of the best answers I ever read! But (as every good answer) it raises some new questions about how to properly program in X. For example, how expensive is the GC? Or how to properly process ShmCompletionEvent in the main events loop? (You can see some commented code there - remaining of my unsuccessful attempt to do it). Do you think it worths to ask separate questions on these?Alanealanine
@Alanealanine I had a few notes about cleanup/better because I thought some things were broken, but realized code worked but might be less than optimal, so removed for post. After the fix is in, it would probably rate a new question, but it would need to be bounded/specific here. They may be less foppish about open ended how-to-improve questions on codereview.stackexchange.com [I've been working on answer for guy there]. Let me think about it, and I'll post update here regarding improvements & techniques to measure, etc.Coarctate
Well, the library is far from optimal yet. :) BTW, some of your notes are not exactly right. For example, the XPending call in ProcessSystemEvents is needed because this function can be called from the user code, not after WaitForSystemEvents and in this case I need to check in order to not block waiting for events. But maybe XPending is superfluous and I need to use XQLength instead.Alanealanine
Ah, yes, notice that this is not Linux only library. It is aimed to be portable with as little as possible OS dependent code. It is designed from this point of view.Alanealanine
@Alanealanine XEventsQueued is the most flexible replacement for XPending [you can control flush/probe with server]. In general, look at which event calls will force a flush and probe server and try to minimize the number of [unnecessary] round trips. That's how only getting an event (where you have XNextEvent now) in one place can help. The "not optimal" I was talking about were the loops on XCheckTypedWindowEvent in .moveresize and .mousemove because the call will do server roundtrip only to find out "no more" (i.e. you want last, but maybe better way to do it from dispatcher)Coarctate
Handling ShmCompetionEvent in the main events loop is good as an idea, but have some problems. The XShmCompletionEvent structure contains only the server side ID of the shared memory, but I don't have way to determine what is the client side ID in order to implement some locking mechanism through mutexes or something.Alanealanine
@Alanealanine shmid is from your shmget and then there is shmseg. Only shmseg is in event struct, but it is also in XShmSegmentInfo which you pass to XShmAttach. So, either you specify shmseg before call, or get it back as return from XShmAttach [makes no sense otherwise]. So, I think you can do it. Write some test code to verify this. Another way, is to block everybody, do a dummy shmputimage after attach, wait for event, and note shmseg in event struct.Coarctate
It can be done only if I implement some list with all allocated images (they can be many) and then on ShmCompletionEvent search in this list in order to find what image caused the event. This is pretty big and complex code, and I am not sure the performance gain will be so big as a result. On the other hand, in the cases where the performance is important - i.e. X server on the network connection, the XSHM extension is not available at all.Alanealanine
@Alanealanine shminfo is already part of your TImage struct, so you're already saving it.Coarctate
No, look, you misunderstand the idea. Yes, the shminfo is part of TImage, but there are many TImage structures allocated (can be hundreds) and the program can draw every of them to the window surface. Then the main events loop receives an event where some ID is specified. What TImage have to be unlocked? In order to find it, I have to check every of the allocated TImage structures, comparing the IDs. But how to loop through TImages, if there is no array (list) that to contain the pointers to the allocated TImage structures?Alanealanine
@Alanealanine Instead of "pendflag", have "pendlist"--just those that have done shmputimage but not yet received confirming event back. Can be simple linked list of pointers: struct shmpend { shmpend *next; int shmseg; TImage *img; time_t reqstart; };. shmseg here is just for [cache] performance so you don't have to deref img to get it. Also, generalized a bit, can serve as a "trace" struct to aid debug of anything you want. You were already starting to do this in the existing putimage code.Coarctate
Yes, I know it can be done this way. And definitely will make some tests later.Alanealanine
K
3

Your source code will be the ultimate piece which we can analyse but since I understand Assembly very less, I will give you an answer on macro level. Exact answer is still unknown to me.

I case of too many events only it is creating this issue but not with normal occurrence of events which means you framework is running out of virtual memory or another frame of event is triggered before the previous one releases its memory. In this kind of situations you can do few things

  1. Try to check if there is any memory leak or not.. After one frame of event is over, try to clean up the memory or end that frame object properly before triggering the new one.
  2. You can also develop a mechanism to make the second frame wait for the first frame to get over. In C/C++ we do it using so many synchronization methods like Mutex or select system calls. If you design follows that kind of pattern, then you can do these
  3. If anywhere you have the authority to change the allocated memory given to your window, try to increase it. Because one thing for sure (according to your explanation ) that this is some memory issue.

Replying on Edit 3 It looks like you are calling some method cinvoke. How it is internally handling the even is unknown to me. Why don't you implement it directly in C. I am sure for whatever target that you are working, you will get some Cross-compiler.

Kistner answered 26/11, 2015 at 4:40 Comment(4)
cinvoke is macros for calling the C function. In the high level languages it is simply missing because in these the functions calls are by their names. For example: cinvoke XCheckTypedEvent, [Display], [ShmCompletionEvent], eax is equal to XCheckTypedEvent(Display, ShmCompletionEvent, eax);Alanealanine
I agree but XCheckTypedEvent(Display, ShmCompletionEvent, eax) must have some wrapper code which in turn uses cinvoke but not directly. That wrapper code is I think is the real trick. In Xorg library, they maintained some extra level of precautions to confine it within the prescribed memory model but they in your case pure call of cinvoke may not handle handle all those memory issuesKistner
For Edit 4 In images.asm what is hApplicationDisplay ??Kistner
hApplicationDisplay is a handle of the connection to the X server, obtained on the application startup via call to XOpenDisplay. In "edit3" simplified source I used the name "Display" but only in order to make the example shorter. It is the same variable.Alanealanine

© 2022 - 2024 — McMap. All rights reserved.