Intel sponsors gamedev.net search:   
The Bag of HoldingBy ApochPiQ      
Apoch's Avatar

Apoch
XP: 64,738
Inventory
Special Items: Shpongle | XBox Live
My brain is built of paths and slides and ladders and lasers and I have invited all of you to enter its pavilion. My brain, as you enter, will smell of tangerines and brand-new running shoes.
Page:   1 2 »»

Sunday, March 29, 2009
Well, another GDC has come and gone. It's always a mixed feeling for me when a big show is over; part of me is so exhausted all I want to do is escape and sleep for a week, while another part wishes I could do it all again tomorrow.

The Bad
I normally wouldn't open up a postmortem with the bad news, but this year there was one principal bit of bad news, and it colored the entire week's experience. Everything was just smaller and less energetic this year. There was a lot of open space on the expo floor, a distinct lack of lines for popular talks, and generally a fairly subdued and muted atmosphere to the whole affair. Of course everyone is going to point the finger at the economy, but I think it ran a bit deeper than that; this was just an off year all around.

Naturally, the trip in general involved a few minor glitches - delayed flights, rough weather, and the occasional argument at the front desk over who is actually paying the hotel bill. That was largely irrelevant though, and I'm happy to have made a trip that didn't involve sleeping overnight in an airport or bouncing several checks.


The Good
This was the first year that GDC has put on a summit specifically for AI. I may be biased, as a programmer, but I've always found the AI crowd to be especially awesome. There's no other subgroup of GDC attendees that combines excellent pedigree and achievements with a general willingness to sit down and talk frankly about ideas and technology. There's plenty of famous and accomplished people roaming around the conference, but the AI guys are just so much more approachable and down-to-earth. At least, that's how it seems to me.

The summit itself was great, but even better was the sort of meta-networking that happened after the summit. The AI guys seemed to move in packs, with groups of the same people showing up at various lectures and discussions. That connectedness lasted through all three days of the AI roundtables, which are my personal favorite sessions at GDC. And, of course, we ended the week on a great high note with the AI programmers' dinner.

Of course there was good stuff beyond the AI crowd as well - the GDNet crew is always a blast to hang out with, and with a ninja like Drew around, it's amazing how many places you can worm your way into.

Swag was in general short supply this year, but my favorite bit of souvenirishness is a bright orange "Work Crew" sticker. Apparently, if you blatantly lie to the security guards at the expo floor, you can wander around unchallenged because everyone thinks you're there to help set up/tear down. I intend to exploit this next year for epic awesomeness.

There were plenty of other great bits over the course of the week, but I'll be covering them in detail with my writeups soon. Make sure to hit the GDC coverage page often, because we'll all be churning out articles for the next several days.



Conclusion
GDC was a little slow this year, but still well worth the experience. I will definitely be there next year.


Also, frisbees.

Comments: 0 - Leave a Comment

Link



Thursday, March 26, 2009
Fairly slow day today; had the second session of the AI roundtable this morning, followed by a lot of lurking around on the expo floor and trying to resist the temptations over at the bar.

Things should pick up a bit over the afternoon, though.

I'd be more specific as to what I've been doing, but my brain is in autopilot and refuses to respond to my requests for coherency. Therefore, you'll have to wait until after the con before I get all my stuff written up.

Rest assured, though, that once said writeups become available, you will be drowning in awesomesauce.

Comments: 1 - Leave a Comment

Link



Tuesday, March 24, 2009
Spent the morning in a session on making AI characters more interesting, and then snuck out of the AI summit to go see a day-long tutorial on multithreading techniques for games. Intel has some pretty slick tools coming out soon, which really make it a breeze to find and eliminate common threading issues, as well as some other goodies.

Not sure what the evening holds yet, but chances are it will (at least eventually) involve alcohol.

Comments: 0 - Leave a Comment

Link



Monday, March 23, 2009
I'm officially checked in and hanging out at the press lounge here at GDC. So far everything has been fairly smooth (at least for me) and it promises to be a nice and busy year.

On the downside, nothing terrifically interesting has happened yet, so I really have very little to say.

I'm covering the AI summit today so there should be some good stuff on that front here in a few hours.


Also, if you happen to be here at GDC, definitely track down Superpig. He's doing some kind of crazy webcam-fuelled thing which involves people doing funny stuff on camera.


Right... I now need to update my brain with the locations of all my sessions for the week. Back later!

Comments: 0 - Leave a Comment

Link



Saturday, March 21, 2009
Epoch GDC2009 Preview Release
I've officially completed the GDC 2009 Preview Edition of the Epoch SDK. This release includes quite a few major changes, including the foundations of Epoch's all-important multiprocessing support.

You can get the juicy goodness directly from here on GDNet - or, you can attend GDC2009, track me down, and get a limited edition, signed and numbered CD with your very own copy of the SDK release! Supplies are limited, so act now! Void where prohibited, and probably just void in general.


And now, it is time for a celebratory late night as I sort out my own GDC schedule. Yayy for not sleeping...

Comments: 1 - Leave a Comment

Link



Tuesday, March 17, 2009
I finally got a lockless FIFO implemented for passing messages between Epoch tasks. Like most lockless code, it works by the skin of its teeth, and a little dose or two of pure luck.

Here's the final code for those interested:
class LocklessMailbox
{
public:
	//
	// Construct and initialize the read and write stacks
	//
	LocklessMailbox()
	{
		std::auto_ptr<Node> wh(new Node);
		std::auto_ptr<Node> rh(new Node);

		WriteHead = wh.get();
		WriteHead->Next = NULL;
		WriteHead->Payload = NULL;

		ReadHead = rh.get();
		ReadHead->Next = NULL;
		ReadHead->Payload = NULL;

		wh.release();
		rh.release();
	}

	//
	// Clean up the read, write, and cache stacks, freeing any
	// remaining messages from each stack.
	//
	~LocklessMailbox()
	{
		Release();
	}

public:
	//
	// Register a message from a producer thread. Any number of threads
	// may call this function.
	//
	void AddMessage(MessageInfo* info)
	{
		Node* msgnode = new Node;
		msgnode->Payload = info;

		Node* oldval;
		Node* newval;
		Node* retval;

		do
		{
			msgnode->Next = WriteHead;
			__asm mfence;
			oldval = msgnode->Next;
			newval = msgnode;
			Node** fieldaddr = &(WriteHead);

			_asm
			{
				mfence
				mov eax, oldval
				mov ecx, newval
				mov edx, dword ptr [fieldaddr]

				lock cmpxchg dword ptr[edx], ecx
				mov retval, eax
				mfence
			}
		}
		while(retval != oldval);
	}

	//
	// Retrieve a message from the mailbox.
	// IMPORTANT: only ONE consumer thread (per mailbox) should call this function
	//
	MessageInfo* GetMessage()
	{
		if(!PendingReads.empty())
		{
			Node* readnode = PendingReads.top();
			MessageInfo* payload = readnode->Payload;
			delete readnode;
			PendingReads.pop();
			return payload;
		}

		if(ReadHead->Next == NULL)
			SwapReadAndWrite();

		Node* n = ReadHead;
		while(ReadHead->Next)
		{
			PendingReads.push(n);
			n = n->Next;
			ReadHead = n;
		}

		if(PendingReads.empty())
			return NULL;

		Node* readnode = PendingReads.top();
		MessageInfo* payload = readnode->Payload;
		delete readnode;
		PendingReads.pop();
		return payload;
	}

private:
	//
	// Internal helper for swapping the read/write stacks
	// See class comment for details
	//
	void SwapReadAndWrite()
	{
		Node* oldval;
		Node* newval;
		Node* retval;
		Node* swappedreadhead;

		do
		{
			swappedreadhead = WriteHead;
			__asm mfence;
			oldval = swappedreadhead;
			newval = ReadHead;
			Node** fieldaddr = &(WriteHead);

			_asm
			{
				mfence
				mov eax, oldval
				mov ecx, newval
				mov edx, dword ptr [fieldaddr]

				lock cmpxchg dword ptr[edx], ecx
				mov retval, eax
				mfence
			}
		}
		while(retval != oldval);

		ReadHead = swappedreadhead;
	}

	//
	// Release the mailbox and free any remaining messages
	//
	void Release()
	{
		for(std::stack<Node*>::container_type::iterator iter = PendingReads.c.begin(); iter != PendingReads.c.end(); ++iter)
		{
			delete (*iter)->Payload;
			delete *iter;
		}

		Node* n = WriteHead;
		while(n)
		{
			delete n->Payload;
			Node* nextn = n->Next;
			delete n;
			n = nextn;
		}

		n = ReadHead;
		while(n)
		{
			delete n->Payload;
			Node* nextn = n->Next;
			delete n;
			n = nextn;
		}
 	}

private:
	struct Node
	{
		Node* Next;
		MessageInfo* Payload;
	};

	Node* WriteHead;
	Node* ReadHead;
	std::stack<Node*> PendingReads;
};



(And if you happen to catch a bug, let me know; I'm banking pretty heavily on this code.)


So that takes care of all the big-ticket items on my list for the GDC release; all I have left to do is some thorough code cleanup, some documentation, and then put together the sales pitch documents.

Overall I'm very happy with this release; as usual it's a huge step beyond the previous release, and I already have a sketched-out roadmap for R7 that'll be even cooler.


Right... back to work

Comments: 1 - Leave a Comment

Link



Monday, March 16, 2009
After significant amounts of scouring across the intertron, and after pulling out large clumps of hair, I've managed to get lock-free message passing implemented in Epoch. This is cool because it makes it trivial to throw hundreds of messages around between tasks, and it's much more efficient than locking all the time.

Unfortunately, I have two problems:
  • The implementation is prone to the ABA problem. I have a minor suspicion that this won't actually bite me because of the way messages are tracked, but I'm way too brain-dead to prove it.

  • The implementation is of a LIFO stack. This means that the order of messages is not preserved; i.e. if I send a task the messages A,B,C,D, it may get them in any unpredictable order. I'd really prefer the semantics to be FIFO, so that if I send A,B,C,D then that's exactly the order the target thread reads the messages.


So I'm going to work on shifting towards a FIFO queue; I've got my eye on an implementation that promises to also solve the ABA problem, so that should be good.

All in all, this was actually less of a problem than I expected; I think I'll be done well before the Wednesday deadline. This is good, because the code needs a lot of polish before I'm really comfortable with releasing it at GDC.


Don't touch that remote - we'll be back shortly with yet more Gooey Chewy Epoch Goodness.

Comments: 0 - Leave a Comment

Link



Sunday, March 15, 2009
The bulk of my day has been sacrificed upon the alter of the synchronization gods. I am trying (thus far with little success) to implement a lock-free queue for sending messages between Epoch tasks. While progress is being made, it's slow going, as I'm pretty much entirely in uncharted territory.

As such, I've made the decision that if I don't have a solid lockless implementation done by Wednesday night, I'll go ahead and revert to the lock-based code for the GDC release. That'll give me time to finish all the code polishing and document-writing that needs to be done.


I plan to share the details of the lockless message passing system as soon as I get it working

Comments: 0 - Leave a Comment

Link



Saturday, March 14, 2009
Haven't really had any single big focus lately; instead I'm just working on polishing up existing functionality, doing some code cleanup, and doing the occasional bugfix.

The results of this aren't really easy to show, obviously, but there is one advance - the acceptmsg() function can now be passed a reusable response map which dictates how a task will respond to receiving certain messages from other code.

I also did some tweaking to the grammar so that whitespace is a bit more relaxed. The combination of these two things yields the following update to the task demo program:

//
// TASKS.EPOCH
//
// Demonstration of the multiprocessing capabilities of Epoch
//


entrypoint : () -> ()
{
	task(asyncjob1)
	{
		pi_task()
	}

	task(asyncjob2)
	{
		pi_task()
	}

	task(complexjob)
	{
		acceptmsg
		(
			compute(integer(a), integer(b), integer(c), string(d)) =>
			{
				message(sender(), completed(add(multiply(a, b), c), d))
			}
		)
	}

	message(asyncjob1, calculate("ignored"))
	message(asyncjob1, ignoredmessage("foo", "bar", 42))

	message(asyncjob1, calculate(10000.0))
	message(asyncjob2, calculate(50000.0))

	message(complexjob, compute(5, 4, 2, "Filler"))

	debugwritestring("Please wait, async tasks running...")


	integer(baz, 42)
	debugwritestring(concat("Main task: ", cast(string, baz)))

	responsemap(resulthandler)
	{
		result(real(foo)) => { debugwritestring(concat("Pi result: ", cast(string, foo))) }
		completed(integer(a), string(b)) => { debugwritestring(concat(b, cast(string, a))) }
	}

	acceptmsg(resulthandler)
	acceptmsg(resulthandler)
	acceptmsg(resulthandler)
}


pi_task : () -> ()
{
	acceptmsg(calculate(real(limit)) => { message(sender(), result(pi(limit))) } )
}


pi : (real(denominator_limit)) -> (real(retval, 0.0))
{
	real(denominator, 1.0)
	boolean(isplus, true)

	do
	{
		real(div, 0.0)
		assign(div, divide(4.0, denominator))

		if(equal(isplus, true))
		{
			assign(retval, add(retval, div))
			assign(isplus, false)
		}
		else
		{
			assign(retval, subtract(retval, div))
			assign(isplus, true)
		}

		assign(denominator, add(denominator, 2.0))

	} while(less(denominator, denominator_limit))
}



Unfortunately, somewhere in my mucking about in the parser, I've introduced several memory leaks. So my priority for now is getting those issues squished. From there I've got a few minor improvements to make, and then the Big One: lockless message passing. I'm hoping to get that done in short order so I can take some time before GDC to clean up and polish the code a bit more.


This week is going to be very long, and not nearly long enough.

Comments: 0 - Leave a Comment

Link



Friday, March 13, 2009
Against my better judgment, I decided to press forward on getting message passing functional. It's taken a fair bit of time, but actually it isn't quite as bad as I thought it would be.

At the moment I use a simple mutex-guarded list of messages, referred to as a task's "mailbox". When a message is sent to a task, the mutex is locked, the message (and any applicable data payload) is inserted into the list, and the mutex is released.

This all works pretty well, but obviously will never scale past a couple of simple tasks. My next big project is to switch over to lock-free containers now that I know the basic message passing infrastructure is working correctly.


Before I stumble off to find a flat surface on which to pass out, I will leave you with some tasty code that shows off how the parallelism features of Epoch can be used to trivially split up lengthy calculations across multiple CPU cores. Keep in mind that this is just one of many concurrency features that are planned for the language.

//
// TASKS.EPOCH
//
// Demonstration of the multiprocessing capabilities of Epoch
//


entrypoint : () -> ()
{
	task(asyncjob1)
	{
		pi_task()
	}

	task(asyncjob2)
	{
		pi_task()
	}

	message(asyncjob1, calculate(10000.0))
	message(asyncjob2, calculate(50000.0))

	debugwritestring("Please wait, async tasks running...")


	integer(baz, 42)
	debugwritestring(concat("Main task: ", cast(string, baz)))


	// Do this twice since we have two results to wait for
	// In a real program we'd do something more robust than
	// just copying/pasting the message handler ;-)

	acceptmsg(result(real(foo)) => { debugwritestring(concat("First result: ", cast(string, foo))) } )
	acceptmsg(result(real(foo)) => { debugwritestring(concat("Second result: ", cast(string, foo))) } )
}


pi_task : () -> ()
{
	acceptmsg(calculate(real(limit)) => { message(caller(), result(pi(limit))) } )
}


pi : (real(denominator_limit)) -> (real(retval, 0.0))
{
	real(denominator, 1.0)
	boolean(isplus, true)

	do
	{
		real(div, 0.0)
		assign(div, divide(4.0, denominator))

		if(equal(isplus, true))
		{
			assign(retval, add(retval, div))
			assign(isplus, false)
		}
		else
		{
			assign(retval, subtract(retval, div))
			assign(isplus, true)
		}

		assign(denominator, add(denominator, 2.0))

	} while(less(denominator, denominator_limit))
}



Comments: 0 - Leave a Comment

Link



Wednesday, March 11, 2009
After some extended discussions, I've come up with a system that I'm almost completely happy with.

entrypoint : () -> ()
{

	task(async)
	{
		responsemap(responses)
		{
			some_message(integer(foo)) => { do_stuff(foo) }

			other_message(integer(lots), integer(of), integer(params)) =>
			{
				do()
				lots()
				of()
				stuff()
			}

			exit() => { return() }
		}


		while(true)
		{
			acceptmsg(responses)
		}
	}


	message(async, some_message(42))
	message(async, other_message(1, 2, 3))
	message(async, exit())

	acceptmsg(result(integer(foo)) => { debugwritestring(cast(string, foo)) } )

}




There are some major notable points here:
  • The big thing is the concept of the response map. This is basically a pattern recognition block, where we examine the token used to identify the message, as well as the attached data payload. When a pattern is matched, we fire off the attached code block. (On a completely irrelevant note, the => magic symbol will also appear when it comes time to do lambdas.)

  • acceptmsg() has three overloads. One accepts a named response map; one accepts an anonymous response map which is constructed in-line; and one has no parameters, which is used to discard a message.

  • Named response maps can be reused, so they are effectively a sort of interface contract. This will come into play when it comes time to build the object model for Epoch.

  • Since this is based on pattern recognition, there's no need to define a set of messages beforehand; the interface for the async task is provided entirely by its responsemaps. Similarly, using an anonymous response map allows us to make one-off interactions between running bits of code.



I'm not sure how I'll feel about this in the light of day, but as of midnight it seems pretty darn solid

Comments: 2 - Leave a Comment

Link


I've been toying around with various syntax options for the message passing model. So far I'm not terribly happy with any of them, but I have settled on one of the lesser evils. I'd like to just kick this out and see if anyone has some thoughts on how to improve it.

Unfortunately I can't afford to let this sit around for a few days collecting ideas, since I have such time pressure to get it all implemented and running. However, if some good suggestions arise, I'll definitely do what I can to change the syntax. For now, though, this is what message passing will look like:

entrypoint : () -> ()
{

	task(async)
	{
		integer(my_foo, 0)

		dispatch initialize : integer(foo)) => { assign(my_foo, foo) }
		
		wait(initialize)

		message(master, result(add(my_foo, 42)))
	}

	dispatch result : (integer(foo)) => { debugwritestring(cast(string, foo)) }

	message(async, initialize(4))
	wait(result)
}






2100 Hours
Erugh. The more I look at that code, the worse it sucks.

The first thing that came out of my twisted head looks like this:
entrypoint : () -> ()
{

	task(async)
	{
		integer(my_foo, 0)

		onmessage(initialize(integer(foo)))
		{
			assign(my_foo, foo);
		}
		
		wait(initialize)

		message(master, result(add(my_foo, 42)))
	}


	message(async, initialize(4))


	onmessage(result(integer(foo)))
	{
		debugwritestring(cast(string, foo))
	}

	wait(result)
}





Slightly more readable but still disgusting.

It looks like I may have to violate my own principles and include some new symbols, because this function-style mess is getting ridiculous for complex tasks.

Will post further updates as I come up with (hopefully) better alternatives.


Ten minutes later...

Here's another attempt:

message sendint :
(
	integer(foo)
)


entrypoint : () -> ()
{

	task(async)
	{
		integer(my_foo, 0)

		onmsg(sendint) { assign(my_foo, foo) }
		wait()

		master(sendint, my_foo)		
	}

	async(sendint, 4)

	onmsg(sendint) { debugwritestring(cast(string, result)) }
	wait()
}



On the plus side, I find it nice and compact and pretty readable. On the down side, it's still kind of hard to see how the flow of messages and data works. Also, having to pre-declare messages kind of sucks.

2125 Hours
Another alternative, mainly just a slight tweaking to get the syntax more "Epoch-like":

entrypoint : () -> ()
{

	task(async)
	{
		integer(my_foo, 0)

		onmsg sendint : (integer(foo))
		{ assign(my_foo, foo) }

		wait()

		mastertask(sendint(add(my_foo, 42)))
	}

	async(sendint(99))

	onmsg sendint : (integer(foo))
	{ debugwritestring(cast(string, result)) }

	wait()
}


Still not really happy with it, but it's better than the other options, at least IMHO.

Comments: 1 - Leave a Comment

Link


Static validation for tasks is now complete.

I found a nice, simple solution to the higher-order-function bug - when a function reference is passed to another function, a binding record is placed on the stack. This record basically links the function parameter to an actual function; this record is how the code knows which function to call.

Safety-checking this is simply a matter of having the stack instruction do validation, rather than the function call instruction. Since the stack instruction knows what function it's placing on the stack, we can simply follow that reference and go validate the function itself.

This means that we literally can cover more code during the validation traversal than is actually executable. For instance, even if you do an unspeakable thing inside an if(false) block, the validator will walk into that block and catch you red-handed. You filthy little devil.


The validator puts out some nice contextual information that indicates how and where things went wrong. Here's an example output:

EXEGEN - Epoch development toolkit

Parsing: first pass...
Parsing: second pass...
Performing static safety validations...

VALIDATION FAILURE: Task performs an unsafe operation
Call stack:
   entrypoint()
   pi()

File: .\scratch.epoch Line: 38 Column: 13

                        debugwritestring("Uh oh! I am illegal!")
                        ^



ERROR: Program failed validation.
0 of 1 programs executed successfully.


So we can immediately see why the function failed validation (filename and code position are displayed) as well as the situation under which the failure occurred (call stack). The combination of these two facts should make it very convenient to fix task issues.


With this bit officially done, there's only one major item left on my GDC checklist: message passing. I will spend a few minutes thinking about this as I drift off to sleep, and hopefully tomorrow I can get a finalized design and start banging out the implementation.

From there, it's mostly a matter of documentation and a bit of double-checking to make sure all the examples and test programs work correctly. Once that's done, it's time to start burning CDs with the GDC distribution on them

Comments: 5 - Leave a Comment

Link



Tuesday, March 10, 2009
My happy little scheme to eliminate shared state and cross-task side effects has hit a major speed bump.

The problem, in particular, is that we can't do a naive traversal of the code tree to detect all side effects. As long as we don't use any higher-order functions, it's fine and actually fairly simple to ensure that all side effects are contained correctly.

However, as soon as a higher-order function is involved, we run into problems. Consider this simple case:

entrypoint : () -> ()
{
	task(async1)
	{
		foobar()
	}
}


foobar : () -> ()
{
	shellfunction(borken)	
}


shellfunction : (function op : () -> ()) -> ()
{
	op()
}


borken : () -> ()
{
	debugwritestring("Oh noes! We borked the task checker!")
}



The debugwritestring operation is flagged as unsafe for using from tasks, since it may cause contention for the console. Normally, the usage of an unsafe function would be caught, and a compile-time error would be thrown.

However, since we only invoke the borken function indirectly via a higher-order function, the error is missed. At compile time, the op parameter of shellfunction is not bound to anything, so there is no way to tell what function might be passed. Without any bindings, the function call is ignored, and the program traversal walks right past the obvious error without even blinking.


The solution, of course, is to build a tree of higher-order function calls and test every potential code path that might invoke such a function. This may prove complicated and even slow, but it only needs to be done once - at compile time - and at runtime things will be just as simple and fast.

(Technically, because of the way the VM is architected, you can actually do illegal things by writing EpochASM code directly, or even bytecode. Since a lot of safety checks are only done at compile time, you can do some pretty cool hacks if you're willing to get dirty and play with the VM's guts.)


So my big challenge of the day is to come up with a way to handle the higher-order function case during the validation traversal.

Aside from that one hangup, tasks are working great. My goal is to get message passing implemented within the next week, so I have a few days spare to polish everything up and write the pitch documents for GDC.

Comments: 2 - Leave a Comment

Link


Today was mostly consumed by hunting down a couple of sneaky memory leaks and a crash or two. Somewhere in there I threw in an update from Boost 1.35.0 to 1.38.0, which made some breaking changes to the boost::spirit code used by the parse grammars. I wasted a couple of hours getting all that cleaned up, although there's still one annoying little mystery.

(For those interested, or who may know the solution, it's related to a missing STATIC_ASSERTION_FAILURE template. Right now I'm just providing the template myself since it's pretty simple, but if there's a proper fix I'd love to know.)

Aside from that, no major work got done; just assorted cleanup and making sure all the pieces are still in sync (especially all the divisions of the compiler/assembler/linker chain).


There is some good news for the day, though: I've worked out a clean and powerful way to ensure that side effects do not propagate out of their owner task. In other words, Task A can't screw with Task B's memory, and neither of them can talk to the "master" task which runs the entrypoint function.

This is done by traversing the generated code tree in memory and looking at what functions and operations are being performed. A simple flag records whether or not the traversal is inside a task, or inside a function called from within a task, etc. If this flag is set, any offending accesses to outside memory or certain types of functions is tagged. Once the traversal is finished, each problematic line of code is logged, along with the call chain that resulted in that line of code being evaluated. Code coverage with this method can easily be 100% because of the way the VM is designed.

So once the task safety stuff is in place, it should be basically impossible to introduce a bug into an Epoch program that arises from use of shared state - because there isn't any concept of shared state.

Of course, this also means that tasks are basically useless, until I get the message passing stuff finished. Only then can we reliably move data in and out of parallel tasks.


Once that is done, I plan to write up several documents about the language and a general sales pitch to take to GDC. Considering that's not too far away now, I'll probably be squeezing my schedule to get this done on time. However, if there is time left over, it'd be nice to cram in a couple more features.


Comments: 1 - Leave a Comment

Link

Page:   1 2 »»

All times are ET (US)

In locus hic, omnes res dementes sunt.
 
S
M
T
W
T
F
S
3
12
18
19
20
22
25
27
28
30
31

OPTIONS
Track this Journal

 RSS 

ARCHIVES
July, 2009
June, 2009
May, 2009
April, 2009
March, 2009
February, 2009
January, 2009
October, 2008
September, 2008
August, 2008
July, 2008
June, 2008
May, 2008
April, 2008
March, 2008
February, 2008
January, 2008
December, 2007
November, 2007
October, 2007
September, 2007
August, 2007
July, 2007
June, 2007
May, 2007
April, 2007
March, 2007
February, 2007
January, 2007
December, 2006
November, 2006
October, 2006
September, 2006
August, 2006
July, 2006
June, 2006
May, 2006
April, 2006
March, 2006
February, 2006
January, 2006
December, 2005
November, 2005
October, 2005