Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: e604cd2e069f311a384e592c12c37d046af67bf6 (plain) (blame)
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# *****************************************************************************
# * Copyright (c) 2011, 2013, 2016 Wind River Systems, Inc. and others.
# * All rights reserved. This program and the accompanying materials
# * are made available under the terms of the Eclipse Public License v1.0
# * which accompanies this distribution, and is available at
# * http://www.eclipse.org/legal/epl-v10.html
# *
# * Contributors:
# *     Wind River Systems - initial API and implementation
# *****************************************************************************

import threading
import types
from .. import protocol
from ..channel.Command import Command


class DispatchWrapper(object):
    "Simple wrapper for attribute access and invocation on TCF dispatch thread"
    def __init__(self, inner):
        self.inner = inner

    def __getattr__(self, attr):
        val = protocol.invokeAndWait(getattr, self.inner, attr)
        if type(val) in (types.FunctionType, types.MethodType):
            return DispatchWrapper(val)
        return val

    def __call__(self, *args, **kwargs):
        return protocol.invokeAndWait(self.inner, *args, **kwargs)


class CommandControl(object):
    """Provides a simple interface to send commands to remote services
    and receive results.

    Usage:
    > cmd = CommandControl(channel)
    > cmd.<service>.<command>(<args>)

    Examples:
    # send command, but don't wait for result:
    > cmd.RunControl.suspend("system")
    # getE() returns the result and raises an exception in case of error:
    > result = cmd.RunControl.getChildren(None).getE()
    # to get error and result at the same time, use this form:
    > error, result = cmd.Breakpoints.getIDs()
    """
    def __init__(self, channel, onDone=None, interactive=False):
        self._lock = threading.Condition()
        self._channel = channel
        self._onDone = onDone
        self._interactive = interactive
        self._queue = []
        self._pending = {}
        self._complete = []

    def __getattr__(self, attr):
        val = getattr(self._channel, attr, None)
        if val:
            if self._interactive and type(val) in (types.FunctionType,
                                                   types.MethodType):
                val = DispatchWrapper(val)
            return val
        services = protocol.invokeAndWait(self._channel.getRemoteServices)
        if attr == "services":
            return services
        if attr in services:
            return ServiceWrapper(self, attr)
        raise AttributeError("Unknown service: %s. Use one of %s" %
                             (attr, services))

    def invoke(self, service, command, *args, **kwargs):
        cmd = None
        if not protocol.isDispatchThread():
            if not kwargs.get("async"):
                cmd = protocol.invokeAndWait(self._invoke, service, command,
                                             *args, **kwargs)
                if cmd and self._interactive:
                    return cmd.getE()
            else:
                with self._lock:
                    self._queue.append((service, command, args, kwargs))
                    if len(self._queue) == 1:
                        protocol.invokeLater(self._processQueue)
                return
        return cmd

    def _invoke(self, service, command, *args, **kwargs):
        cmdCtrl = self

        class GenericCommand(Command):
            _result = None

            def done(self, error, args):  # @IgnorePep8
                resultArgs = None
                rcmds = ('read', 'readdir', 'roots')
                if not error and args:
                    # error result is usually in args[0],
                    # but there are exceptions
                    if service == "StackTrace" and command == "getContext":
                        error = self.toError(args[1])
                        resultArgs = (args[0],)
                    elif service == "Expressions" and command == "evaluate":
                        error = self.toError(args[1])
                        resultArgs = (args[0], args[2])
                    elif service == "FileSystem" and command in rcmds:
                        error = self.toError(args[1])
                        resultArgs = (args[0],) + tuple(args[2:])
                    elif service == "Streams" and command == 'read':
                        error = self.toError(args[1])
                        resultArgs = (args[0],) + tuple(args[2:])
                    elif service == "Diagnostics":
                        if command.startswith("echo"):
                            resultArgs = (args[0],)
                    else:
                        error = self.toError(args[0])
                        resultArgs = args[1:]
                cmdCtrl._doneCommand(self.token, error, resultArgs)

            def wait(self, timeout=None):
                cmdCtrl._waitForCommand(self.token, timeout)

            def cancel(self):
                return protocol.invokeAndWait(self.token.cancel)

            def getResult(self, wait=None):
                if wait:
                    cmdCtrl._waitForCommand(self.token)
                return self._result

            def getE(self):
                r = self.getResult(True)
                if r.error:
                    raise r.error
                return r.args

            def get(self):
                r = self.getResult(True)
                return r.args

            def getError(self):
                r = self.getResult(True)
                return r.error

            def __str__(self):
                if self._async:
                    return self.getCommandString()
                return str(self.get())

            def __iter__(self):
                return iter(self.getResult(True))
        cmd = GenericCommand(self._channel, service, command, args)
        cmd._async = kwargs.get("async")
        cmd._onDone = kwargs.get("onDone")
        self._addPending(cmd)
        return cmd

    def _processQueue(self):
        assert protocol.isDispatchThread()
        with self._lock:
            for cmd in self._queue:
                service, command, args, kwargs = cmd
                self._invoke(service, command, *args, **kwargs)
            del self._queue[:]

    def _addPending(self, cmd):
        with self._lock:
            self._pending[cmd.token.id] = cmd
            self._lock.notifyAll()

    def _doneCommand(self, token, error, args):
        with self._lock:
            cmd = self._pending.get(token.id)
            assert cmd
            del self._pending[token.id]
            cmd._result = CommandResult(token, error, args)
            if cmd._async:
                self._complete.append(cmd)
            isDone = self.isDone()
            if isDone:
                self._lock.notifyAll()
        if cmd._onDone:
            if args is None:
                args = (None,)
            cmd._onDone(error, *args)
        if isDone and self._onDone:
            self._onDone()

    def isDone(self):
        with self._lock:
            return not self._pending and not self._queue

    def wait(self, timeout=None):
        assert not protocol.isDispatchThread()
        with self._lock:
            while self._pending or self._queue:
                self._lock.wait(timeout)
                if timeout:
                    break

    def _waitForCommand(self, token, timeout=None):
        assert not protocol.isDispatchThread()
        with self._lock:
            while token.id in self._pending:
                self._lock.wait(timeout)
                if timeout:
                    break
            else:
                if self._queue:
                    self._lock.wait(timeout)
                    while token.id in self._pending:
                        self._lock.wait(timeout)
                        if timeout:
                            break

    def cancel(self):
        if not protocol.isDispatchThread():
            protocol.invokeLater(self.cancel)
            return
        with self._lock:
            for cmd in list(self._pending.values()):
                cmd.token.cancel()
            del self._queue[:]

    def getResult(self, wait=True):
        if wait:
            self.wait()
        with self._lock:
            result = [c.getResult() for c in self._complete]
            del self._complete[:]
        return result


class CommandResult(object):
    def __init__(self, token, error, args):
        self.token = token
        self.error = error
        # unwrap result if only one element
        if args and len(args) == 1:
            args = args[0]
        self.args = args

    def __str__(self):
        if self.error:
            return "[%s] error: %s" % (self.token.id, self.error)
        return "[%s] result: %s" % (self.token.id, self.args)
    __repr__ = __str__

    def __iter__(self):
        yield self.error
        yield self.args


class ServiceWrapper(object):
    def __init__(self, control, service):
        self._control = control
        self._service = service

    def __getattr__(self, attr):
        return CommandWrapper(self._control, self._service, attr)


class CommandWrapper(object):
    def __init__(self, control, service, command):
        self._control = control
        self._service = service
        self._command = command

    def __call__(self, *args, **kwargs):
        return self._control.invoke(self._service, self._command, *args,
                                    **kwargs)

Back to the top