Blog Closed

This blog has moved to Github. This page will not be updated and is not open for comments. Please go to the new site for updated content.
Showing posts with label Contexts. Show all posts
Showing posts with label Contexts. Show all posts

Wednesday, September 30, 2009

Future Flow Control and PCC Hackathon

Contexts are PMCs now. They aren't really usable from PIR yet in a real sense, but the first steps have been taken. Continuations should be more introspectable from PIR. ExceptionHandlers are in line for a major overhaul, and the PCC refactors promise to (eventually) redo the way we pass arguments to and fro. Things are changing in the world of Parrot control flow, and the user is going to be the real winner from all of it.

So the question comes up: What will Parrot's next generation control flow look like? What kinds of exotic control flow will be be able to support? What kinds should be aim to support? What kinds of cool new paradigms could we invent? We have Subroutines, Methods, Multimethods, Coroutines, Continuations, ExceptionHandlers and Closures. What can we get from combining these? What benefits can people see by being able to introspect Contexts and CallSignatures from running PIR code?

An ExceptionHandler Coroutine is one that I've been particularly interested in:

.sub 'WhenBadThingsHappen' :handles('foo')
say "One exception!"
.yield()
say "Two exceptions!"
.yield()
say "Too many exceptions!"
exit 0
.end

Syntax of course will vary, but you can see what I am planning there.

ExceptionHandler subroutines are going to need at least two possible resolutions: Either they properly handle the exception and resume normal control flow (by jumping to a preset resume Continuation or Sub), or they rethrow the exception. A rethrown exception moves to the next handler, and when there are no more handlers to catch it we exit the runloop and rethrow in the previous runloop (or exit with an unhandled exception if there are no more runloops).

In current ExceptionHandlers, we can use ".get_results" to get access to the thrown exception object only. What if ExceptionHandlers were instead curried Continuations that we could use to pass all sorts of arbitrary arguments to it?

$P0 = new ['ExceptionHandler']
push $P0, arg1
push $P0, arg2
push_eh $P0

We have a newclosure opcode which clones a sub and captures it's lexical scope. What if we had a newhandler opcode which cloned an existing sub, captured it's lexical scope, and marked it as a handler? That would allow us to use any arbitrary sub as an exception handler, treated as a lexically enclosed scope of the function where it is created?

With a little bit of work Contexts are going to be usable and introspectable from PIR code. Bacek has already done some work making data from Contexts visible to PIR code, which is a step in the right direction. What kinds of things will we be able to do with these, and what new things do we want? Manually manage registers? Iterate over registers like an array? Call another function but force it to use the same context (preinitialized registers)? Direct manipulation of control flow ("Jump up 3 call frames immediately")? Easy recursion of anonymous functions? Runtime creation and manipulation of LexPad and LexInfo? Change the current invocant object? Direct manipulation of the list of currently registered exception handlers? Redirect the call chain so we "return" to somewhere besides where we were called from?

That last idea is actually kind of fun, because you can start to think about Subs as being an intermediate step in a jump. "Go here, but do X on the way". I call a Sub, but the call gets intercepted by one of these filter functions and the parameter list is manipulated in a way that is invisible to both the caller and the callee. We can also start to think about PIR-based argument passing and processing. Need argument handling that Parrot doesn't provide natively? Write a small PIR argument preprocessor function and insert it invisibly into every function call!

This Saturday, 2 October, we're going to host a little PCC-related hackathon. A bunch of the Parrot hackers will be around, working on the PCC refactors branch, fixing PCC bugs, and hopefully implementing some long-awaited features. If you're a committer and have some time to hack on Saturday, we can use all the manpower we can get: Debugging and fixing test failures, profiling and optimizing codepaths, triaging and deleting old dead code, writing new tests to prevent regression, testing HLLs, and implementing new features. This will also be a great time for newcomers to get involved with Parrot too: Help out with a concerted development project, meet the developers, start getting your hands on the code, and start learning about one of Parrot's core mechanisms. A good time will be had by all!

Thursday, September 3, 2009

Context PMC Follow Up


Quick followup this morning: bacek has merged the context_pmc3 branch into trunk just now in r40958. Testing would be much appreciated!

Wednesday, September 2, 2009

Refactoring Continuations and Contexts

I've written, on average, about 1.5 posts per day in the last two weeks, but haven't managed to post any of them. Sorry about the delay!

When 1.5.0 was released, the Sub and Continuation PMCs were just garbage collectible wrappers around the C structs Parrot_sub and Parrot_cont respectively. My initial attempt at a Context PMC would have followed this same mold, applying a thin garbage-collectible wrapper around the Parrot_context structure. This was certainly not ideal for a number of reasons (multiple allocations, extra layers of pointer dereferencing, lots of extra pointer-checking code to make sure everything is where we think it is, etc), but it would have helped to plug the memory leaks introduced by the reference-counting context system as-is.

Shortly after 1.5.0 was released, however, bacek's branch to clean up the Sub PMC landed, and got rid of the Parrot_sub structure entirely (converting all it's fields into attributes for the PMC). He quickly started work on the Context PMC next, throwing out what little work I had done and moved towards a comprehensive Context refactor. He wasn't just turning the Context PMC into a thin wrapper, he was converting Parrot to have a proper Context PMC wholesale. It was what I was planning to do, though I would have spread it out over several steps and several branches.

His work created a little bit of a stir because it ran afoul of the deprecation policy: Some users were poking directly into the guts of the Parrot_context structure, and his work would have broken that. Nobody said "no", but there was definitely some stalling and arguing about the finer points of the deprecation policy. Plus, he had a weird failure in JIT (Which I will discuss in depth with my next post), so he couldn't merge immediately anyway.

A few days ago I saw a very cool diff from newcomer jrtayloriv to perform the conversion for the Continuation PMC and the Parrot_cont structure. The diff wasn't completely ready to apply to trunk yet, but it was an amazing start and came out of the blue absolutely unexpected. What his patch did was merge the fields of the Parrot_cont structure into the attributes of the Continuation PMC, and replace references to "Parrot_cont" with "Parrot_Continuation_attributes". It's not a huge change to be sure, and it doesn't do anything to address the poor encapsulation of the Continuation PMC, but it's a great first step in a comprehensive cleanup and refactor of that system. I created the kill_parrot_cont branch to test that patch, and after a few more patches from jrtayloriv and help from some other commiters it matured nicely.

Tonight, both branches became 100% ready to merge. chromatic fixed the JIT failure in bacek's branch, and also "clarified" the deprecation policy to smooth the way for these changes. It helped that many of Parrot's users, especially Rakudo, were strongly in favor of a merge sooner rather then later. jrtayloriv got all the rest of his tests passing and cleaned up things that needed it, and decided to get the branch merged before making too many more changes. I sent an email to the list about these two, and am waiting for some feedback. Hopefully we can get both branches reviewed and approved by the end of this week, which gives us about a week and a half to clean up the wreckage and get trunk in shape for the 1.6.0.

1.6.0 is certainly going to be an eventful release. A lot of good branches have merged already and more are on the way. On top of that, we're seeing some great interest from newcomers that is translating to real code being applied. Hopefully we'll have a few new committers in the next coming weeks to help us with all these ambitious changes that we are making.

Wednesday, August 5, 2009

Contexts: Path Forward and Problems

I mentioned the ongoing work for the Contexts PMC stuff in a previous post, and wanted to elaborate on that a little more here. Currently, as I've mentioned before elsewhere, context structures in Parrot are just basic data structures that are manually managed using an idiosyncratic reference counting scheme. The goal is to turn Contexts into a normal PMC type so they can be garbage collected, because currently we leak a lot of memory through miscounted contexts.

Coke started a branch to do the conversion, and I quickly realized that we are going to need to do the work in baby steps to get it right. It's really multiple projects: Encapsulating the Context PMC behind a good interface, cleaning up all the associated logic, possibly separating out the context structure based on it's various purposes, and then converting everything over to be a garbage-collectable PMC. It's worth mentioning here that there is a faster way to do this, but faster does note equate to better, or correct in terms of proper design, so I don't bother to discuss it here.

Contexts serve a number of purposes in Parrot:
  1. They manage the register sets, and provide access to the Parrot registers. A new context allocates storage space for the register types, does the pointer arithmetic to access them, marks their contents for garbage collection, and cleans them up when the context goes out of scope.
  2. Contexts hold pointer items to a variety of other data items: the current namespace PMC. The current invocant PMC on a method, the current return continuation PMC, current argument and return signatures, the current lexpad, and the current HLL ID.
  3. Contexts form a type of linked list (actually, more of an acyclic graph) that represents the call chain. Every context also contains a pointer to an enclosing context, such as is the case in a closure.
  4. Contexts keep track of control flow by holding a reference to the current PC, and also hold fields of flags that determine which errors and warnings are enabled currently.
  5. Contexts provide access to the current list of constants in the packfile.
You can see that these uses aren't exactly orthogonal to one another but at the same time it's not strictly necessary that all this information be stored in exactly one data structure either. For instance, we could separate out the organizational aspects (call chain, current PC, current errors/warnings, access to constants, pointers to namespace/lexpad/sub/invocant PMCs) into one PMC type and separate out the register management functions into a different PMC type. This would have the benefit of allowing unrelated activities to be stored in separate PMCs, and would allow all the necessary functions to be hidden behind a reasonable VTABLE interface without too much shoehorning. I'm not saying that this is what we will do or even what we should do, but all possibilities are worth thinking about at this juncture.

It's especially worthwhile to think about when you consider the severe limitations stipulated by the VTABLE interface, which was clearly designed with "data" PMCs (Integer, String, Float) in mind and not "process" types like this. Start divviing up the various VTABLE functions and you run out pretty quickly. We can use the get_integer_keyed_int, get_string_keyed_int, get_number_keyed_int, and get_pmc_keyed_int interfaces, and the set_* equivalents to access the register set. The invoke VTABLE could be used to directly activate the RetContinuation, preventing us from needing a fast accessor for that item. We can still have an accessor for it, but it doesn't need to be a fast or dedicated one. The push and pop vtables can be used to manage the context stack for the common case of simple subroutine calls and returns.

That is, unfortunately, where things start getting messy. We could conceivably use the get_*_keyed_str as delegate accessors for the LexPad PMCs, to access lexical variables by name without needing a fast accessor to retrieve the LexPad PMC itself (again, we can have an accessor, but it doesn't need to be a hugely fast one). This single type of accessor doesn't give us the flexibility to restrict a search for a lexical variable to either the current context or all :outer containing contexts, but it's a start.

And even then, how do we access the current Sub PMC, the current NameSpace, the current invocant object, the Outer context (for closures), the parameter and return signatures, etc? The pcc_rewiring branch may offer some answers here, by encapsulating the current invocant, current parameter and current return signatures, and maybe other things. That's fine and good in the short term, but the long-term plan is to merge the Context PMC and the CallSignature PMC together into a single object that represents a subroutine state (with functionality to call into and call out of that state as well). And then we're back in the same mess with more data then we have interfaces to access it all.

Of course, this is all speculative, and it's a few steps away from where we need to be focusing right now. This kind of thinking does help to inform our short-term decisions, but we cannot be mired in them. Instead of finding creative and perhaps obnoxious ways to shoehorn all this functionality into the limited VTABLE interface, maybe instead we need to think about creating a different, and more appropriate, abstraction layer for Contexts. In short, we need to create an API for them.

Creating the abstraction layer should be very straight-forward since we know the kinds of things that we need to access. Once we have it, we can start migrating parts of Parrot to use the new interface instead of poking into the context structure directly. And once all accesses use the new interface, we can change the mechanics behind that interface to anything we want (such as converting the Contexts to a PMC type).

I posted a quick file to the trac ticket a few days ago that demonstrates some ideas that this API could follow and also hopefully demonstrates how easy one would be to create. It's not complete and it's not pretty, but it shows how it could be done. I will mention one possible problem area being in the .ops files, where several macros (IREG, NREG, PREG, SREG) are used to access registers as l-values and r-values interchangably, which makes a function-based "don't poke into my structures directly" interface kind of troublesome. So in the foreseeable future we will probably have to allow some of these same kinds of accesses, although it really would be nice to fix the OPS compiler to avoid these kinds of issues entirely.

So the first step is to create some kind of API for contexts, and then start the long and arduous process of converting Parrot's core codebase to use that API instead. Hopefully by that time pcc_rewiring will have landed and we can start planning the next steps.