This file is indexed.

/usr/lib/python2.7/dist-packages/xpra/gtk_common/nested_main.py is in xpra 0.15.8+dfsg-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# This file is part of Xpra.
# Copyright (C) 2008 Nathaniel Smith <njs@pobox.com>
# Copyright (C) 2013, 2014 Antoine Martin <antoine@devloop.org.uk>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.


# A utility system for cooperation among stuff that needs nested main-loops.
#
# So here's the problem: in the clipboard code, and eventually maybe in other
# places too, we have to define a function that
#   1) gets called
#   2) makes a request over the wire
#   3) gets the response to that request
#   4) and *then* returns to its caller
# And of course, we don't want to block the whole application between (2) and
# (3). So we have to make the request, and then enter the main-loop
# recursively. Eventually, once the response is received, we need to exit that
# main loop.
#
# That's easy enough. The trouble comes when we need to handle multiple such
# requests at the same time. (E.g., suppose the user attempts to paste twice
# in quick succession.) Some responses might come out of order; some might
# never come. But, because of how Python works, it doesn't matter -- we have
# to return from these functions in strict LIFO order.
#
# So, for instance, if we send out request 1, then request 2, and then get
# response 1... we can't just call gtk.main_quit(), because that will actually
# drop us back into request *2*'s handler function. The correct thing to do is
# to stash the response somewhere and wait. Then, when we get response 2, we
# should call gtk.main_quit() *twice*, since both responses are now available.
# (However, as an additional complication, you can't just call gtk.main_quit()
# twice, because that just makes you quit the current main loop. Two times,
# which is the same as doing it once. So we have to return to the main loop
# between each call to gtk.main_quit().)
#
# Also, suppose it happens that response 2 never arrives. Then not only will
# we never exit the inner main-loop... we will never exit the outer main-loop
# either! Response 1 will just sit there and never get processed, even though
# it has arrived.
#
# Solution: indirection!
#
# Specifically, we keep track of the 'state' of each such nested call. Each
# call can be in one of 3 states:
#   -- done
#   -- soft timed out
#   -- hard timed out
# The soft-timeout is the maximum amount of time that this nested call can
# block other nested calls from completion. The hard-timeout is the maximum
# amount of time that we'll wait for this nested call, period. (However, this
# might be extended if this nested call ends up blocked on other calls. If the
# nested call finishes after the hard time out expires, but before we actually
# get around to processing it, then it'll still be processed as normal -- not
# as timed out.)
#
# There actually is no guarantee on how long a nested call ends up blocked,
# even in the face of soft timeouts, because new nested calls might keep
# coming in, each extending the total blocking period by their soft
# timeout. If the user ever stops pasting madly for a few seconds, though,
# then everything should have a chance to return to equilibrium...

from xpra.gtk_common.gobject_compat import import_gobject, import_gtk
gobject = import_gobject()
gtk = import_gtk()

from xpra.log import Logger
log = Logger("gtk")

# For debugging:
def _dump_recursion_info():
    # This is how deeply nested in recursive main-loops we are:
    print("gtk depth: %s" % (gtk.main_level()))
    import ctypes
    class ThreadState(ctypes.Structure):
        _fields_ = [("next", ctypes.c_voidp),
                    ("interp", ctypes.c_voidp),
                    ("frame", ctypes.c_voidp),
                    ("recursion_depth", ctypes.c_int)]
    ts = ctypes.cast(ctypes.pythonapi.PyThreadState_Get(),
                     ctypes.POINTER(ThreadState))
    # This is Python's count of the recursion depth -- i.e., the thing that
    # will eventually trigger the 'Maximum recursion depth exceeded' message.
    print("python internal depth: %s" % ts[0].recursion_depth)
    import inspect
    count = 0
    frame = inspect.currentframe()
    while frame:
        frame = frame.f_back
        count += 1
    # This is the number of frames in the current stack. It probably only
    # counts up to the last call to gtk.main().
    print("stack frames: %s" % count)
    # The number of NestedMainLoops in progress:
    print("NestedMainLoops: %s" % len(NestedMainLoop._stack))

class NestedMainLoop(object):
    _stack = []

    def __str(self):
        return  "NestedMainLoop(%#x)" % id(self)

    @classmethod
    def _quit_while_top_done(cls):
        if len(cls._stack)==0:
            log("NestedMainLoop: no more nested loops")
            #no more nested loops
            return False
        assert gtk.main_level()>1
        top = cls._stack[-1]
        #if another loop is done,
        #we need to pop the ones above it that have timedout,
        #starting with "top", so we can get to it:
        done_pending = bool([o for o in cls._stack if o!=top and o._done])
        log("NestedMainLoop: top loop=%#x, done=%s, soft timeout=%s, hard timeout=%s, done_pending=%s",
            id(top), top._done, top._hard_timed_out, top._soft_timed_out, done_pending)
        if top._done or top._hard_timed_out or \
            (top._soft_timed_out and done_pending):
            log("exiting nested main loop %#x, leaving level %s",
                id(top), gtk.main_level())
            gtk.main_quit()
            return True     #check new top of stack again
        #not done / timedout yet:
        return False

    def _wakeup(self):
        gobject.timeout_add(0, self._quit_while_top_done)

    def _soft_timeout_cb(self):
        log("%#x: soft timeout", id(self))
        self._soft_timed_out = True
        self._wakeup()

    def _hard_timeout_cb(self):
        log("%#x: hard timeout", id(self))
        self._hard_timed_out = True
        self._wakeup()

    def done(self, result):
        log("%#x: done: %s", id(self), result)
        self._result = result
        self._done = True
        self._wakeup()

    # Returns whatever was passed to done(), or None in the case of a
    # timeout.
    def main(self, soft_timeout, hard_timeout):
        self._result = None
        self._done = False
        self._soft_timed_out = False
        self._hard_timed_out = False
        self._stack.append(self)
        soft = gobject.timeout_add(soft_timeout, self._soft_timeout_cb)
        hard = gobject.timeout_add(hard_timeout, self._hard_timeout_cb)
        log("Entering nested loop %#x (level %s)",
            id(self), gtk.main_level())
        try:
            gtk.main()
            log("%#x: returned from nested main loop", id(self))
        finally:
            assert self._stack.pop() is self
            if not self._soft_timed_out:
                gobject.source_remove(soft)
            if not self._hard_timed_out:
                gobject.source_remove(hard)
        log("%s: done=%#x, soft=%s, hard=%s, result=%s",
            id(self), self._done, self._soft_timed_out, self._hard_timed_out, self._result)
        return self._result