def recv(self, atom, args): from typhon.objects.collections.maps import EMPTY_MAP if atom is FROMNOW_1: duration = promoteToDouble(args[0]) p, r = makePromise() vat = currentVat.get() uv_timer = ruv.alloc_timer(vat.uv_loop) # Stash the resolver. ruv.stashTimer(uv_timer, (vat, r)) # repeat of 0 means "don't repeat" ruv.timerStart(uv_timer, resolveTimer, int(duration * 1000), 0) assert ruv.isActive(uv_timer), "Timer isn't active!?" return p if atom is TRIAL_1: obj = args[0] then = time.time() obj.call(u"run", []) now = time.time() # We can't give the value up immediately, due to determinism # requirements. Instead, provide it as a promise which will be # available on subsequent turns. rv = DoubleObject(now - then) p, r = makePromise() vat = currentVat.get() vat.sendOnly(r, RESOLVE_1, [rv], EMPTY_MAP) return p raise Refused(self, atom, args)
def makeProcess(executable, args, environment): """ Create a subordinate process on the current node from the given executable, arguments, and environment. """ # Third incarnation: libuv-powered and requiring bytes. executable = unwrapBytes(executable) # This could be an LC, but doing it this way fixes the RPython annotation # for the list to be non-None. argv = [] for arg in unwrapList(args): s = unwrapBytes(arg) assert s is not None, "proven impossible by hand" argv.append(s) env = {} for (k, v) in unwrapMap(environment).items(): env[unwrapBytes(k)] = unwrapBytes(v) packedEnv = [k + '=' + v for (k, v) in env.items()] vat = currentVat.get() try: process = ruv.allocProcess() sub = SubProcess(vat, process, argv, env) ruv.spawn(vat.uv_loop, process, file=executable, args=argv, env=packedEnv) sub.retrievePID() return sub except ruv.UVError as uve: raise userError(u"makeProcess: Couldn't spawn process: %s" % uve.repr().decode("utf-8"))
def whenResolvedOnly(self, o, callback): from typhon.objects.collections.maps import EMPTY_MAP vat = currentVat.get() vat.sendOnly(o, _WHENMORERESOLVED_1, [WhenResolvedReactor(callback, o, None, vat)], EMPTY_MAP) return NullObject
def setContents(self, data): sibling = self.temporarySibling(".setContents") p, r = makePromise() vat = currentVat.get() path = sibling.asBytes() # Use CREAT | EXCL to cause a failure if the temporary file # already exists. flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL with io: f = 0 try: f = ruv.magic_fsOpen(vat, path, flags, 0777) except object as err: smash(r, StrObject(u"Couldn't open file fount: %s" % err)) else: try: writeLoop(f, data) except object as err: ruv.magic_fsClose(vat, f) smash(r, StrObject(u"libuv error: %s" % err)) else: ruv.magic_fsClose(vat, f) ruv.magic_fsRename(vat, path, self.asBytes()) resolve(r, NullObject) return p
def makeStdErr(): vat = currentVat.get() uv_loop = vat.uv_loop tty = ruv.alloc_tty(uv_loop, 2, False) # ruv.TTYSetMode(tty, ruv.TTY_MODE_RAW) return StreamDrain(ruv.rffi.cast(ruv.stream_tp, tty), vat)
def run(self, executable, argv, env, stdinFount=None, stdoutDrain=None, stderrDrain=None, stdin=False, stdout=False, stderr=False): # Sixth incarnation: Now with streamcaps! # Unwrap argv. l = [] for arg in argv: bs = unwrapBytes(arg) assert bs is not None, "proven impossible" l.append(bs) argv = l # Unwrap and prep environment. d = {} for (k, v) in env.items(): d[unwrapBytes(k)] = unwrapBytes(v) packedEnv = [k + '=' + v for (k, v) in d.items()] env = d vat = currentVat.get() # Set up the list of streams and attach streamcaps. stdinSink = nullSink stdoutSource = stderrSource = emptySource streams = [] if stdin: stream = ruv.rffi.cast(ruv.stream_tp, ruv.alloc_pipe(vat.uv_loop)) streams.append(stream) wrapped = ruv.wrapStream(stream, 1) stdinSink = StreamSink(wrapped, vat) else: streams.append(nullptr(ruv.stream_t)) if stdout: stream = ruv.rffi.cast(ruv.stream_tp, ruv.alloc_pipe(vat.uv_loop)) streams.append(stream) wrapped = ruv.wrapStream(stream, 1) stdoutSource = StreamSource(wrapped, vat) else: streams.append(nullptr(ruv.stream_t)) if stderr: stream = ruv.rffi.cast(ruv.stream_tp, ruv.alloc_pipe(vat.uv_loop)) streams.append(stream) wrapped = ruv.wrapStream(stream, 1) stderrSource = StreamSource(wrapped, vat) else: streams.append(nullptr(ruv.stream_t)) try: process = ruv.allocProcess() sub = SubProcess(vat, process, argv, env, stdin=stdinSink, stdout=stdoutSource, stderr=stderrSource) vat.enqueueEvent(SpawnProcessIOEvent( vat, sub, process, executable, argv, packedEnv, streams)) return sub except ruv.UVError as uve: raise userError(u"makeProcess: Couldn't spawn process: %s" % uve.repr().decode("utf-8"))
def run(self, unused): from typhon.objects.collections.maps import EMPTY_MAP if self.done: return if self._ref.isResolved(): if isBroken(self._ref): f = self._eb else: f = self._cb try: outcome = f.call(u"run", [self._ref]) if not isBroken(self._ref) and isBroken(outcome): # success arm returned a broken promise outcome = self._eb.call(u"run", [outcome]) self._resolver.resolve(outcome) except UserException as ue: from typhon.objects.exceptions import sealException if not isBroken(self._ref): # success arm threw try: self._resolver.resolve(self._eb.call(u"run", [UnconnectedRef(currentVat.get(), sealException(ue))])) except UserException as ue2: self._resolver.smash(sealException(ue2)) else: # failure arm threw self._resolver.smash(sealException(ue)) self.done = True else: self.vat.sendOnly(self._ref, _WHENMORERESOLVED_1, [self], EMPTY_MAP)
def whenBroken(self, o, callback): from typhon.objects.collections.maps import EMPTY_MAP p, r = makePromise() vat = currentVat.get() vat.sendOnly(o, _WHENMORERESOLVED_1, [WhenBrokenReactor(callback, o, r, vat)], EMPTY_MAP) return p
def whenNear(self, o, callback): from typhon.objects.collections.maps import EMPTY_MAP p, r = makePromise() vat = currentVat.get() vat.sendOnly(o, _WHENMORERESOLVED_1, [WhenNearReactor(callback, o, r, vat)], EMPTY_MAP) return p
def makeProxy(handler, resolutionBox, resolved): if not handler.isSettled(): raise userError(u"Proxy handler not settled") vat = currentVat.get() if resolved: return FarRef(vat, handler, resolutionBox) else: return RemotePromise(vat, handler, resolutionBox)
def _whenMoreResolved(self, callback): from typhon.objects.collections.maps import EMPTY_MAP # Welcome to _whenMoreResolved. # This method's implementation, in Monte, should be: # to _whenMoreResolved(callback): callback<-(self) vat = currentVat.get() vat.sendOnly(callback, RUN_1, [self], EMPTY_MAP) return NullObject
def makeStdOut(): vat = currentVat.get() uv_loop = vat.uv_loop tty = ruv.alloc_tty(uv_loop, 1, False) # XXX works exactly as expected, including disabling most TTY signal # generation # ruv.TTYSetMode(tty, ruv.TTY_MODE_RAW) return StreamDrain(ruv.rffi.cast(ruv.stream_tp, tty), vat)
def measureTimeTaken(self, f): """ Like m`f<-()`, but returns a promise for a pair `[result, timeTaken :Double]` in seconds. """ from typhon.objects.collections.maps import EMPTY_MAP vat = currentVat.get() return vat.send(TimeMeasurer(f), RUN_0, [], EMPTY_MAP)
def makeReactorStats(): """ Compute some information about the reactor. """ # XXX what a hack from typhon.vats import currentVat loop = currentVat.get().uv_loop return LoopStats(loop)
def __init__(self, code, frame, globals): self.code = code self.env = Environment(frame, globals, self.code.localSize(), promote(self.code.maxDepth), promote(self.code.maxHandlerDepth)) # For vat checkpointing. from typhon.vats import currentVat self.vat = currentVat.get()
def makeStdIn(): vat = currentVat.get() uv_loop = vat.uv_loop stdinKind = ruv.guess_handle(0) if stdinKind == ruv.HANDLE_TTY: stdin = ruv.alloc_tty(uv_loop, 0, True) return StreamFount(ruv.rffi.cast(ruv.stream_tp, stdin), vat) else: return FileFount(ruv.alloc_fs(), 0, vat)
def makeStdErr(): vat = currentVat.get() uv_loop = vat.uv_loop stderrKind = ruv.guess_handle(2) if stderrKind == ruv.HANDLE_TTY: tty = ruv.alloc_tty(uv_loop, 2, False) # ruv.TTYSetMode(tty, ruv.TTY_MODE_RAW) return StreamDrain(ruv.rffi.cast(ruv.stream_tp, tty), vat) else: return FileDrain(ruv.alloc_fs(), 2, vat)
def stdout(self): vat = currentVat.get() uv_loop = vat.uv_loop kind = ruv.guess_handle(1) if kind == ruv.HANDLE_TTY: stdout = ruv.alloc_tty(uv_loop, 1, False) stream = ruv.wrapStream(ruv.rffi.cast(ruv.stream_tp, stdout), 1) return TTYSink(stdout, stream, vat) else: return FileSink(1, vat)
def getAddrInfo(node, service): node = unwrapBytes(node) service = unwrapBytes(service) vat = currentVat.get() gai = ruv.alloc_gai() p, r = makePromise() ruv.stashGAI(gai, (vat, r)) ruv.getAddrInfo(vat.uv_loop, gai, gaiCB, node, service, nullptr(ruv.s.addrinfo)) return p
def rename(self, dest): p, r = makePromise() vat = currentVat.get() uv_loop = vat.uv_loop fs = ruv.alloc_fs() src = self.asBytes() ruv.stashFS(fs, (vat, r)) ruv.fsRename(uv_loop, fs, src, dest, renameCB) return p
def fromNow(self, duration): p, r = makePromise() vat = currentVat.get() uv_timer = ruv.alloc_timer(vat.uv_loop) now = ruv.now(vat.uv_loop) # Stash the resolver. ruv.stashTimer(uv_timer, (vat, (r, now))) # repeat of 0 means "don't repeat" ruv.timerStart(uv_timer, resolveTimer, int(duration * 1000), 0) return p
def stderr(self): vat = currentVat.get() uv_loop = vat.uv_loop kind = ruv.guess_handle(2) if kind == ruv.HANDLE_TTY: stderr = ruv.alloc_tty(uv_loop, 2, False) stream = ruv.wrapStream(ruv.rffi.cast(ruv.stream_tp, stderr), 1) return StreamSink(stream, vat) else: return FileSink(ruv.alloc_fs(), 2, vat)
def sendAll(self, atom, args, namedArgs): vat = currentVat.get() # Think about it: These args are in the current vat, and we're # accessing them from our target's vat. Therefore, these refs should # be from our target's vat into the current vat. refs = packLocalRefs(args, vat, self.targetVat) namedRefs = packLocalNamedRefs(namedArgs, vat, self.targetVat) return packLocalRef( self.targetVat.send(self.target, atom, refs, namedRefs), self.targetVat, vat)
def stdin(self): vat = currentVat.get() uv_loop = vat.uv_loop kind = ruv.guess_handle(0) if kind == ruv.HANDLE_TTY: stdin = ruv.alloc_tty(uv_loop, 0, True) stream = ruv.wrapStream(ruv.rffi.cast(ruv.stream_tp, stdin), 1) return TTYSource(stdin, stream, vat) else: return FileSource(0, vat)
def sendAll(self, atom, args, namedArgs): vat = currentVat.get() # Think about it: These args are in the current vat, and we're # accessing them from our target's vat. Therefore, these refs should # be from our target's vat into the current vat. refs = packLocalRefs(args, vat, self.targetVat) namedRefs = packLocalNamedRefs(namedArgs, vat, self.targetVat) return packLocalRef(self.targetVat.send(self.target, atom, refs, namedRefs), self.targetVat, vat)
def stderr(self): vat = currentVat.get() uv_loop = vat.uv_loop kind = ruv.guess_handle(2) if kind == ruv.HANDLE_TTY: stderr = ruv.alloc_tty(uv_loop, 2, False) stream = ruv.wrapStream(ruv.rffi.cast(ruv.stream_tp, stderr), 1) return TTYSink(stderr, stream, vat) else: return FileSink(2, vat)
def stdin(self): vat = currentVat.get() uv_loop = vat.uv_loop kind = ruv.guess_handle(0) if kind == ruv.HANDLE_TTY: stdin = ruv.alloc_tty(uv_loop, 0, True) stream = ruv.wrapStream(ruv.rffi.cast(ruv.stream_tp, stdin), 1) return StreamSource(stream, vat) else: return FileSource(ruv.alloc_fs(), 0, vat)
def makeStdOut(): vat = currentVat.get() uv_loop = vat.uv_loop stdoutKind = ruv.guess_handle(1) if stdoutKind == ruv.HANDLE_TTY: tty = ruv.alloc_tty(uv_loop, 1, False) # XXX works exactly as expected, including disabling most TTY signal # generation # ruv.TTYSetMode(tty, ruv.TTY_MODE_RAW) return StreamDrain(ruv.rffi.cast(ruv.stream_tp, tty), vat) else: return FileDrain(ruv.alloc_fs(), 1, vat)
def recv(self, atom, args): from typhon.objects.collections.maps import EMPTY_MAP if atom is UNSAFENOW_0: return DoubleObject(time.time()) if atom is FROMNOW_1: duration = promoteToDouble(args[0]) p, r = makePromise() vat = currentVat.get() uv_timer = ruv.alloc_timer(vat.uv_loop) # Stash the resolver. ruv.stashTimer(uv_timer, (vat, r)) # repeat of 0 means "don't repeat" ruv.timerStart(uv_timer, resolveTimer, int(duration * 1000), 0) assert ruv.isActive(uv_timer), "Timer isn't active!?" return p if atom is SENDTIMESTAMP_1: now = time.time() vat = currentVat.get() return vat.send(args[0], RUN_1, [DoubleObject(now)], EMPTY_MAP) raise Refused(self, atom, args)
def rename(self, fr): if not isinstance(fr, FileResource): raise userError(u"rename/1: Must be file resource") dest = fr.asBytes() p, r = makePromise() vat = currentVat.get() with io: try: ruv.magic_fsRename(vat, self.asBytes(), dest) except object as err: smash(r, StrObject(u"Couldn't rename file: %s" % err)) else: resolve(r, NullObject) return p
def sendOnly(self, target, verb, args, namedArgs): """ Send a message to an object. The message will be delivered on some subsequent turn. """ namedArgs = resolution(namedArgs) if not isinstance(namedArgs, ConstMap): raise WrongType(u"namedArgs must be a ConstMap") # Signed, sealed, delivered, I'm yours. sendAtom = getAtom(verb, len(args)) vat = currentVat.get() vat.sendOnly(target, sendAtom, args, namedArgs)
def mirandaMethods(self, atom, arguments, namedArgsMap): from typhon.objects.collections.maps import EMPTY_MAP if atom is _CONFORMTO_1: # Welcome to _conformTo/1. # to _conformTo(_): return self return self if atom is _GETALLEGEDINTERFACE_0: # Welcome to _getAllegedInterface/0. interface = self.optInterface() if interface is None: from typhon.objects.interfaces import ComputedInterface interface = ComputedInterface(self) return interface if atom is _PRINTON_1: # Welcome to _printOn/1. from typhon.objects.constants import NullObject self.printOn(arguments[0]) return NullObject if atom is _RESPONDSTO_2: from typhon.objects.constants import wrapBool from typhon.objects.data import unwrapInt, unwrapStr verb = unwrapStr(arguments[0]) arity = unwrapInt(arguments[1]) atom = getAtom(verb, arity) result = (atom in self.respondingAtoms() or atom in mirandaAtoms) return wrapBool(result) if atom is _SEALEDDISPATCH_1: # to _sealedDispatch(_): return null from typhon.objects.constants import NullObject return NullObject if atom is _UNCALL_0: from typhon.objects.constants import NullObject return NullObject if atom is _WHENMORERESOLVED_1: # Welcome to _whenMoreResolved. # This method's implementation, in Monte, should be: # to _whenMoreResolved(callback): callback<-(self) from typhon.vats import currentVat vat = currentVat.get() vat.sendOnly(arguments[0], RUN_1, [self], EMPTY_MAP) from typhon.objects.constants import NullObject return NullObject return None
def recv(self, atom, args): if atom is GETCONTENTS_0: return self.open(openGetContentsCB, flags=os.O_RDONLY, mode=0000) if atom is SETCONTENTS_1: data = unwrapBytes(args[0]) sibling = self.temporarySibling(".setContents") p, r = makePromise() vat = currentVat.get() uv_loop = vat.uv_loop fs = ruv.alloc_fs() path = sibling.asBytes() # Use CREAT | EXCL to cause a failure if the temporary file # already exists. flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL sc = SetContents(vat, data, r, sibling, self) ruv.stashFS(fs, (vat, sc)) ruv.fsOpen(uv_loop, fs, path, flags, 0777, openSetContentsCB) return p if atom is OPENFOUNT_0: return self.open(openFountCB, flags=os.O_RDONLY, mode=0000) if atom is OPENDRAIN_0: # Create the file if it doesn't yet exist, and truncate it if it # does. Trust the umask to be reasonable for now. flags = os.O_CREAT | os.O_WRONLY # XXX this behavior should be configurable via namedarg? flags |= os.O_TRUNC return self.open(openDrainCB, flags=flags, mode=0777) if atom is RENAME_1: fr = args[0] if not isinstance(fr, FileResource): raise userError(u"rename/1: Must be file resource") return self.rename(fr.asBytes()) if atom is SIBLING_1: name = unwrapStr(args[0]) if u'/' in name: raise userError(u"sibling/1: Illegal file name '%s'" % name) return self.sibling(name.encode("utf-8")) if atom is TEMPORARYSIBLING_0: return self.temporarySibling(".new") raise Refused(self, atom, args)
def listen(self, handler): vat = currentVat.get() uv_server = ruv.alloc_tcp(vat.uv_loop) try: ruv.tcpBind(uv_server, "0.0.0.0", self.port) except ruv.UVError as uve: raise userError(u"makeTCP4ServerEndpoint: Couldn't listen: %s" % uve.repr().decode("utf-8")) uv_stream = ruv.rffi.cast(ruv.stream_tp, uv_server) ruv.stashStream(uv_stream, (vat, handler)) # XXX hardcoded backlog of 42 ruv.listen(uv_stream, 42, connectionCB) return TCP4Server(uv_server)
def open(self, callback, flags=None, mode=None): # Always call this as .open(callback, flags=..., mode=...) assert flags is not None assert mode is not None p, r = makePromise() vat = currentVat.get() uv_loop = vat.uv_loop fs = ruv.alloc_fs() path = self.asBytes() log.log(["fs"], u"makeFileResource: Opening file '%s'" % path.decode("utf-8")) ruv.stashFS(fs, (vat, r)) ruv.fsOpen(uv_loop, fs, path, flags, mode, callback) return p
def connect(self): vat = currentVat.get() stream = ruv.alloc_tcp(vat.uv_loop) fount, fountResolver = makePromise() drain, drainResolver = makePromise() # Ugh, the hax. resolvers = ConstList([fountResolver, drainResolver]) ruv.stashStream(ruv.rffi.cast(ruv.stream_tp, stream), (vat, resolvers)) # Make the actual connection. ruv.tcpConnect(stream, self.host, self.port, connectCB) # Return the promises. return ConstList([fount, drain])
def send(self, target, verb, args, namedArgs): """ Send a message to an object, returning a promise for the message delivery. The promise will be fulfilled after successful delivery, or smashed upon error. The message will be delivered on some subsequent turn. """ namedArgs = resolution(namedArgs) if not isinstance(namedArgs, ConstMap): raise WrongType(u"namedArgs must be a ConstMap") # Signed, sealed, delivered, I'm yours. sendAtom = getAtom(verb, len(args)) vat = currentVat.get() return vat.send(target, sendAtom, args, namedArgs)
def listenStream(self, handler): vat = currentVat.get() uv_server = ruv.alloc_tcp(vat.uv_loop) try: if self.inet_type == 4: ruv.tcp4Bind(uv_server, "0.0.0.0", self.port) elif self.inet_type == 6: ruv.tcp6Bind(uv_server, "::", self.port) except ruv.UVError as uve: raise userError(u"listenStream/1: Couldn't listen: %s" % uve.repr().decode("utf-8")) uv_stream = ruv.rffi.cast(ruv.stream_tp, uv_server) ruv.stashStream(uv_stream, (vat, handler)) # XXX hardcoded backlog of 42 ruv.listen(uv_stream, 42, connectionStreamCB) return TCPServer(uv_server)
def connectStream(self): """ Connect this endpoint, returning a `[source, sink]` pair.of vows. """ vat = currentVat.get() stream = ruv.alloc_tcp(vat.uv_loop) source, sourceResolver = makePromise() sink, sinkResolver = makePromise() # Ugh, the hax. resolvers = wrapList([sourceResolver, sinkResolver]) ruv.stashStream(ruv.rffi.cast(ruv.stream_tp, stream), (vat, resolvers)) # Make the actual connection. if self.inet_type == 4: ruv.tcp4Connect(stream, self.host, self.port, connectStreamCB) elif self.inet_type == 6: ruv.tcp6Connect(stream, self.host, self.port, connectStreamCB) # Return the promises. return [source, sink]
def send(ref, atom, args, namedArgs): if isinstance(ref, Promise): return ref.sendAll(atom, args, namedArgs) else: vat = currentVat.get() return vat.send(ref, atom, args, namedArgs)
def writeTracePreamble(self): vat = currentVat.get() self.writeLine(u"TRACE: From vat " + vat.name)
def sendOnly(ref, atom, args, namedArgs): if isinstance(ref, Promise): ref.sendAllOnly(atom, args, namedArgs) else: vat = currentVat.get() vat.sendOnly(ref, atom, args, namedArgs)