copied everything over from 2012 and removed all of the actual robot code except the drivetrain stuff


git-svn-id: https://robotics.mvla.net/svn/frc971/2013/trunk/src@4078 f308d9b7-e957-4cde-b6ac-9a88185e7312
diff --git a/aos/atom_code/async_action/AsyncAction.cpp.inc b/aos/atom_code/async_action/AsyncAction.cpp.inc
new file mode 100644
index 0000000..911bee8
--- /dev/null
+++ b/aos/atom_code/async_action/AsyncAction.cpp.inc
@@ -0,0 +1,405 @@
+#include <sys/syscall.h>
+#include <errno.h>
+#include <math.h>
+
+namespace aos {
+
+template<class T, class S> AsyncAction<T, S>::AsyncAction(const std::string name) : local_count(0), local_done_status(0), local_pid(0), 
+	done_index(0), status_index(0), next_status_count(0){
+	aos_type_sig async_action_status_sig = {sizeof(async_action_status<S>), 1234, 1};
+	status_queue = aos_fetch_queue(name.c_str(), &async_action_status_sig);
+	aos_type_sig async_action_start_sig = {sizeof(async_action_start<T>), 4321, 1};
+	start_queue = aos_fetch_queue(name.c_str(), &async_action_start_sig);
+}
+
+template<class T, class S> uint16_t AsyncAction<T, S>::Start(T &args){
+	CheckStop();
+	// write something to the start queue and increment the count in the status queue
+	async_action_start<S> *start = (async_action_start<S> *)aos_queue_get_msg(start_queue);
+	if(start == NULL)
+		return 0;
+	memcpy(&start->args, &args, sizeof(T));
+	async_action_status<T> *status = (async_action_status<T> *)aos_queue_read_msg(status_queue, 0); // wait until one starts it if didn't already
+	uint16_t r = 0;
+	aos_resource_entity *entity;
+	if(status == NULL)
+		goto err;
+	r = status->count + 1;
+	local_pid = status->pid;
+	start->count = r;
+	entity = status->resource_entity;
+	aos_queue_free_msg(status_queue, status);
+	status = (async_action_status<T> *)aos_queue_get_msg(status_queue);
+	status->count = r;
+	status->done_status = 0;
+	status->pid = local_pid;
+	status->resource_entity = entity; // the resource_entity of the action, not the caller!
+	start->parent = resource_entity; // if it's NULL, it'll get fixed when starting
+	if(aos_queue_write_msg(status_queue, status, OVERRIDE) < 0){
+		goto err;
+	} else {
+		status = NULL;
+	}
+	if(aos_queue_write_msg(start_queue, start, OVERRIDE) < 0){
+		goto err;
+	} else {
+		start = NULL;
+	}
+err:
+	if(start != NULL)
+		aos_queue_free_msg(start_queue, start);
+	if(status != NULL)
+		aos_queue_free_msg(status_queue, status);
+	local_count = r;
+	return r;
+}
+template<class T, class S> void AsyncAction<T, S>::Stop(int32_t count){
+	CheckStop();	
+	async_action_status<S> *status = (async_action_status<S> *)aos_queue_read_msg(status_queue, PEEK | NON_BLOCK);
+	if(status == NULL)
+		return;
+	local_pid = status->pid;
+	local_count = status->count;
+	aos_queue_free_msg(status_queue, status);
+	if(local_count != GetCount(count)) // if it's the wrong one
+		return;
+	async_action_start<S> *start = (async_action_start<S> *)aos_queue_read_msg(start_queue, PEEK | NON_BLOCK);
+	if(start == NULL) // if there isn't something in the start queue (aka one running)
+		return;
+	aos_queue_free_msg(start_queue, start);
+	union sigval sival;
+	sival.sival_int = 0;
+	if(sigqueue(local_pid, STOP_SIGNAL, sival) < 0){ // if sending the signal failed
+		fprintf(stderr, "sigqueue STOP_SIGNAL (which is %d) with pid %d failed with errno %d: ", STOP_SIGNAL, local_pid, errno);
+		perror(NULL);
+	}
+}
+
+template<class T, class S> bool AsyncAction<T, S>::IsDone(int32_t count){
+	CheckStop();
+	async_action_status<S> *status = (async_action_status<S> *)aos_queue_read_msg(status_queue, PEEK | NON_BLOCK);
+	// below = there is one running && it's the right one && it's done
+	bool r = status != NULL && status->count == GetCount(count) && ((status->done_status & 0x02) != 0);
+	aos_queue_free_msg(status_queue, status);
+	return r;
+}
+template<class T, class S> uint16_t AsyncAction<T, S>::Join(int32_t count_in){
+	const uint16_t count = GetCount(count_in);
+	if(count == 0){
+		fprintf(stderr, "not joining non-existent run 0\n");
+		return 0;
+	}
+	async_action_status<S> *status = NULL;
+	done_index = 0; // the queue's message numbering might have been reset
+	do {
+		aos_queue_free_msg(status_queue, status);
+		CheckStop();
+		status = (async_action_status<S> *)aos_queue_read_msg_index(status_queue, FROM_END, &done_index);
+	} while(status != NULL && (status->done_status & 0x02) == 0 && (status->count == count));
+	if(status == NULL){
+		fprintf(stderr, "bad news at %s: %d\n", __FILE__, __LINE__);
+		return 0;
+	}
+	aos_queue_free_msg(status_queue, status);
+	return count;
+}
+template<class T, class S> bool AsyncAction<T, S>::GetNextStatus(S &status_out, int32_t count_in){
+	async_action_status<S> *status = NULL;
+start:
+	CheckStop();
+	const uint16_t count = GetCount(count_in);
+	if(count != next_status_count){ // reset the index if another one gets started in case the queue indexes get reset
+		next_status_count = count;
+		status_index = 0;
+	}
+	status = (async_action_status<S> *)aos_queue_read_msg_index(status_queue, FROM_END, &status_index);
+	if(status == NULL)
+		goto start;
+	if(status->count != count){
+		aos_queue_free_msg(status_queue, status);
+		return false;
+	}
+	bool r = (status->done_status & 0x01) != 0;
+	memcpy(&status_out, &status->status, sizeof(S));
+	aos_queue_free_msg(status_queue, status);
+	return r;
+}
+template<class T, class S> bool AsyncAction<T, S>::GetStatus(S &status_out, int32_t count){
+	CheckStop();
+	async_action_status<S> *status = (async_action_status<S> *)aos_queue_read_msg(status_queue, PEEK | NON_BLOCK);
+	if(status == NULL)
+		return false;
+	if(status->count != GetCount(count)){
+		aos_queue_free_msg(status_queue, status);
+		return false;
+	}
+	bool r = (status->done_status & 0x01) != 0;
+	memcpy(&status_out, &status->status, sizeof(S));
+	aos_queue_free_msg(status_queue, status);
+	return r;
+}
+
+template<class T, class S> void AsyncAction<T, S>::PostStatus(S &status_in){
+	CheckStop();
+	async_action_status<S> *status = (async_action_status<S> *)aos_queue_get_msg(status_queue);
+	if(status == NULL)
+		return;
+	memcpy(&local_status, &status_in, sizeof(S));
+	memcpy(&status->status, &status_in, sizeof(S));
+	status->done_status = 0x01;
+	local_done_status = 0x01;
+	status->pid = local_pid;
+	status->resource_entity = resource_entity;
+	if(aos_queue_write_msg(status_queue, status, OVERRIDE) < 0){
+		local_done_status = 0;
+		memset(&local_status, 0x00, sizeof(S));
+		aos_queue_free_msg(status_queue, status);
+	}
+}
+template<class T, class S> template<int (*O)(aos_resource_entity *, aos_resource *)> inline bool AsyncAction<T, S>::ResourceOp(uint16_t resource){
+	CheckStop();
+	if(resource_entity == NULL){
+		fprintf(stderr, "no started AsyncAction detected in this process\n");
+		return false;
+	}
+	switch(O(resource_entity, aos_resource_get(resource))){
+		case 0:
+			break;
+		case 1:
+			return false;
+		case -1:
+			throw resourceexception();
+	}
+	return true;
+}
+template<class T, class S> void AsyncAction<T, S>::RequestResource(uint16_t resource){
+	if(ResourceOp<aos_resource_request>(resource)){
+		resources[resource] = 1;
+	}else{
+		throw resourceexception(); // if we can't get a resource, we just want to stop
+	}
+}
+template<class T, class S> bool AsyncAction<T, S>::TryRequestResource(uint16_t resource){
+	if(ResourceOp<aos_resource_request>(resource)){
+		resources[resource] = 1;
+		return true;
+	}else{
+		return false;
+	}
+}
+template<class T, class S> void AsyncAction<T, S>::ReleaseResource(uint16_t resource){
+	if(ResourceOp<aos_resource_release>(resource)){
+		if(resources.erase(resource) != 1)
+			fprintf(stderr, "warning: value for resource %d wasn't 1\n", resource);
+	}
+}
+
+#if 0
+// from gcc's (used 4.6.2) libjava/include/i386-signal.h
+/* We use kernel_sigaction here because we're calling the kernel
+   directly rather than via glibc.  The sigaction structure that the
+   syscall uses is a different shape from the one in userland and not
+   visible to us in a header file so we define it here.  */
+extern "C" 
+{
+  struct kernel_sigaction 
+  {
+    void (*k_sa_sigaction)(int,siginfo_t *,void *);
+    unsigned long k_sa_flags;
+    void (*k_sa_restorer) (void);
+    sigset_t k_sa_mask;
+  };
+}
+#define RESTORE(name, syscall) RESTORE2 (name, syscall)
+#define RESTORE2(name, syscall)			\
+asm						\
+  (						\
+   ".text\n"					\
+   ".byte 0  # Yes, this really is necessary\n" \
+   "	.align 16\n"				\
+   "__" #name ":\n"				\
+   "	movl $" #syscall ", %eax\n"		\
+   "	int  $0x80"				\
+   );
+/* The return code for realtime-signals.  */
+RESTORE (restore_rt, __NR_rt_sigreturn)
+void restore_rt (void) asm ("__restore_rt")
+  __attribute__ ((visibility ("hidden")));
+#define INIT_SIGNAL(signal, sigaction)				\
+do								\
+  {								\
+    struct kernel_sigaction act;				\
+    act.k_sa_sigaction = sigaction;				\
+    sigemptyset (&act.k_sa_mask);				\
+    act.k_sa_flags = SA_SIGINFO|0x4000000;			\
+    act.k_sa_restorer = restore_rt;				\
+    syscall (SYS_rt_sigaction, signal, &act, NULL, _NSIG / 8);	\
+  }								\
+while (0)  
+/* Unblock a signal.  Unless we do this, the signal may only be sent
+   once.  */
+static void 
+unblock_signal (int signum __attribute__ ((__unused__)))
+{
+  sigset_t sigs;
+
+  sigemptyset (&sigs);
+  sigaddset (&sigs, signum);
+  sigprocmask (SIG_UNBLOCK, &sigs, NULL);
+}
+#endif
+
+template<class T, class S> void AsyncAction<T, S>::sig_action(int signum, siginfo_t *, void *){
+  // TODO really shouldn't be using stdio in here (check before changing)
+	/*unblock_signal(signum);
+	// MAKE_THROW_FRAME(exception)
+	std::exception *exception = new std::exception;
+	throw exception;*/
+	fprintf(stderr, "received signal %d\n", signum);
+	if(signum == STOP_SIGNAL)
+		interrupt |= 0x01;
+	else if(signum == RESOURCE_KILL_SIGNAL)
+		interrupt |= 0x02;
+	else if(signum == SIGINT || signum == SIGTERM)
+		interrupt |= 0x04;
+	else
+		fprintf(stderr, "unknown signal %d\n", signum);
+}
+template<class T, class S> int AsyncAction<T, S>::Run(uint8_t priority) {
+	interrupt = 0;
+	struct sigaction sigact;
+	sigact.sa_sigaction = sig_action;
+	sigact.sa_flags = 0;
+	sigaction(STOP_SIGNAL, &sigact, NULL);
+	sigaction(RESOURCE_KILL_SIGNAL, &sigact, NULL);
+	sigaction(SIGINT, &sigact, NULL);
+	sigaction(SIGTERM, &sigact, NULL); // kill from the command line default
+
+	if(resource_entity != NULL){
+		fprintf(stderr, "resource_entity isn't already null, which means that this is the second Run being called in this process or something (which isn't allowed...)\n");
+		return -1;
+	}
+	resource_entity = aos_resource_entity_create(priority);
+
+	async_action_status<S> *status = (async_action_status<S> *)aos_queue_get_msg(status_queue);
+	if(status == NULL)
+		return -1;
+	//memset(status + offsetof(aync_action_status<S>, status->status), 0x00, sizeof(S));
+	status->count = 1;
+	status->done_status = 0;
+	local_done_status = 0;
+	local_pid = getpid();
+	fprintf(stderr, "local_pid=%d STOP_SIGNAL is currently %d\n", local_pid, STOP_SIGNAL);
+	status->pid = local_pid;
+	status->resource_entity = resource_entity;
+	// put the initial status in
+	if(aos_queue_write_msg(status_queue, status, OVERRIDE) < 0){
+		aos_queue_free_msg(status_queue, status);
+		return -1;
+	}
+	
+	// clear out any pending start messages
+	async_action_start<T> *start = (async_action_start<T> *)aos_queue_read_msg(start_queue, NON_BLOCK);
+	aos_queue_free_msg(start_queue, start);
+
+	OnStart();
+
+	T args;
+	uint16_t current_count;
+	while(true){
+		interrupt = 0;
+		// wait for a new start message
+		start = (async_action_start<T> *)aos_queue_read_msg(start_queue, PEEK);
+		if(start == NULL){
+			if(interrupt & 0x04)
+				break;
+			continue;
+		}
+		memcpy(&args, &start->args, sizeof(T));
+		current_count = start->count;
+		if(start->parent == NULL){ // Start isn't getting called from a process with an AsyncAction implementation running in it
+			start->parent = aos_resource_entity_root_get();
+		}
+		aos_resource_entity_set_parent(resource_entity, start->parent);
+		aos_queue_free_msg(start_queue, start);
+		status = (async_action_status<S> *)aos_queue_get_msg(status_queue);
+		if(status == NULL){
+			fprintf(stderr, "%s: %d: could not get a status message to write initial status to\n", __FILE__, __LINE__);
+			continue;
+		}
+		status->done_status = local_done_status = 0;
+		status->pid = local_pid;
+		status->count = current_count;
+		status->resource_entity = resource_entity;
+		if(aos_queue_write_msg(status_queue, status, OVERRIDE) < 0)
+			aos_queue_free_msg(status_queue, status);
+
+		try {
+			DoAction(args);
+		} catch (stopexception &e) {
+			fprintf(stderr, "caught stopexception\n");
+		} catch (resourceexception &e) {
+			fprintf(stderr, "caught resourceexception\n");
+		} catch (...) {
+			fprintf(stderr, "caught another exception... (bad news)\n");
+		}
+
+		start = (async_action_start<T> *)aos_queue_read_msg(start_queue, NON_BLOCK);
+		if(start == NULL){
+			fprintf(stderr, "somebody else consumed the start message (at %s: %d)\n", __FILE__, __LINE__);
+		} else {
+			aos_queue_free_msg(start_queue, start);
+		}
+
+		status = (async_action_status<S> *)aos_queue_get_msg(status_queue);
+		if(status == NULL){
+			fprintf(stderr, "couldn't get a message to write that we're finished in to %s: %d\n", __FILE__, __LINE__);
+			continue;
+		}
+		memcpy(&status->status, &local_status, sizeof(S));
+		status->done_status = local_done_status | 0x02;
+		status->pid = local_pid;
+		status->count = current_count;
+		status->resource_entity = resource_entity;
+		if(aos_queue_write_msg(status_queue, status, OVERRIDE) < 0)
+			aos_queue_free_msg(status_queue, status);
+
+		std::map<uint16_t, uint8_t>::iterator it;
+		for(it = resources.begin(); it != resources.end(); ++it){
+			ReleaseResource(it->first);
+		}
+		if(!resources.empty()){
+			fprintf(stderr, "resources isn't empty after releasing everything in it. clearing it\n");
+			resources.clear();
+		}
+
+		if(interrupt & 0x04)
+			break;
+	}
+	OnEnd();
+	return 0;
+}
+
+template<class T, class S> void AsyncAction<T, S>::Sleep(double seconds){
+	timespec ts;
+	clock_gettime(CLOCK_MONOTONIC, &ts);
+	ts.tv_sec += (time_t)floor(seconds);
+	ts.tv_nsec += (long)((seconds - floor(seconds)) * 1000000000);
+	if(ts.tv_nsec > 1000000000){
+		ts.tv_nsec -= 1000000000;
+		ts.tv_sec += 1;
+	}
+	int rv;
+	do {
+		CheckStop();
+		rv = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL);
+	} while(rv);
+}
+
+template<class T, class S> void AsyncAction<T, S>::DoAction(__attribute__((unused)) T &args){
+	fprintf(stderr, "this (at %s: %d) should never get run\n", __FILE__, __LINE__);
+	*((int *)NULL) = 0;
+}
+
+} // namespace aos
+
diff --git a/aos/atom_code/async_action/AsyncAction.h b/aos/atom_code/async_action/AsyncAction.h
new file mode 100644
index 0000000..31be206
--- /dev/null
+++ b/aos/atom_code/async_action/AsyncAction.h
@@ -0,0 +1,155 @@
+#ifndef _AOS_ASYNC_ACTION_H_
+#define _AOS_ASYNC_ACTION_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+
+#include <string>
+#include <map>
+#include <type_traits>
+
+#include "aos/aos_core.h"
+#include "aos/common/type_traits.h"
+
+class AsyncActionTest;
+
+namespace aos {
+	// S is status type T is parameter type
+
+	template<class S> struct async_action_status {
+		S status;
+		uint8_t done_status; // 1 = valid status, 2 = done
+		uint16_t count;
+		pid_t pid;
+		aos_resource_entity *resource_entity;
+	};
+	template<class T> struct async_action_start {
+		T args;
+		uint16_t count;
+		aos_resource_entity *parent;
+	};
+
+	class AsyncActionRunner;
+	class ResourceAction_t;
+
+	class AsyncActionStatics {
+		friend class ResourceAction_t; // a testing AsyncAction that has to mess with resource stuff
+		protected:
+			class stopexception : public std::exception {
+				virtual const char* what() const throw() {
+					return "This exception indicates that the AsyncAction was stopped. This message should never show up anywhere.";
+				}
+				public:
+				stopexception() : std::exception() {}
+			};
+			class resourceexception : public std::exception {
+				virtual const char* what() const throw() {
+					return "This exception indicates that the AsyncAction was stopped due to resource contention. This message should never show up anywhere.";
+				}
+				public:
+				resourceexception() : std::exception() {}
+			};
+
+			// 1 = stop current DoAction
+			// 2 = stop corrent DoAction because of resource issues
+			// 4 = SIGINT pending (nicely close everything down first)
+			static volatile uint8_t interrupt;
+			static const int STOP_SIGNAL;
+			static aos_resource_entity *resource_entity;
+
+			// throw any exceptions indicated by signals
+			static inline void CheckStop(){
+				if(interrupt & (0x01 | 0x04)) {
+					throw stopexception();
+					fprintf(stderr, "the code should never ever get here (%s: %d)\n", __FILE__, __LINE__);
+				}else if(interrupt & 0x02) {
+					throw resourceexception();
+					fprintf(stderr, "the code should never ever get here (%s: %d)\n", __FILE__, __LINE__);
+				}
+			}
+	};
+
+	// S and T have to be structs
+	// T is sTart and S is status
+	// public functions (except for constructor) should be called on this AsyncAction
+	// in processes other than the one Run ing this AsyncAction
+	// vice versa for protected ones
+	template<class T, class S> class AsyncAction : public AsyncActionStatics {
+		static_assert(shm_ok<async_action_start<T>>::value,
+                  "T must go through shared memory");
+		static_assert(shm_ok<async_action_status<S>>::value,
+                  "S must go through shared memory");
+		friend class AsyncActionRunner;
+		friend class ::AsyncActionTest;
+		public:
+		AsyncAction(const std::string name);
+
+		// returns what the count will be throughout this run
+		// return of 0 indicates error (didn't start)
+		uint16_t Start(T &args);
+
+		// -1 for count means use the static one
+		// aka !IsRunning()
+		bool IsDone(int32_t count = -1);
+		// returns which one it joined
+		uint16_t Join(int32_t count = -1);
+		// return is whether there is an actual status or just garbage
+		bool GetStatus(S &status_out, int32_t count = -1) __attribute__ ((warn_unused_result));
+		// waits for a new good status
+		bool GetNextStatus(S &status_out, int32_t count = -1) __attribute__ ((warn_unused_result));
+
+		void Stop(int32_t count = -1);
+		protected:
+		// starts infinite loop that waits for Starts
+		// returns 0 for success, negative for error
+		// gets called by generated code
+		int Run(uint8_t priority);
+
+		virtual void DoAction(T &args);
+		// should only be called from DoAction
+		void PostStatus(S &status_in);
+		void RequestResource(uint16_t resource);
+		// returns whether succeeded
+		bool TryRequestResource(uint16_t resource);
+		void ReleaseResource(uint16_t resource);
+
+		// called at the beginning and end of Run
+		virtual void OnStart() {}
+		virtual void OnEnd() {}
+
+		// this should be the only sleep (or variant thereof) that gets called
+		void Sleep(double seconds);
+		private:
+		aos_queue *status_queue, *start_queue;
+
+		uint16_t local_count;
+		S local_status;
+		uint8_t local_done_status;
+		pid_t local_pid;
+
+		template<int (*O)(aos_resource_entity *, aos_resource *)> bool ResourceOp(uint16_t resource);
+		std::map<uint16_t, uint8_t> resources;
+
+		// for read_msg_index
+		int done_index, status_index;
+		uint16_t next_status_count; // uses it to figure out when to reset status_index
+
+		// return the default if appropriate
+		inline uint16_t GetCount(int32_t in){
+			if(in < 0)
+				return local_count;
+			else
+				return (uint16_t)in;
+		}
+
+		static void sig_action(int, siginfo_t *, void *);
+	};
+
+} // namespace aos
+
+#include "AsyncAction.cpp.inc" // to make the template stuff work
+
+#endif
diff --git a/aos/atom_code/async_action/AsyncActionHandle.h b/aos/atom_code/async_action/AsyncActionHandle.h
new file mode 100644
index 0000000..8088848
--- /dev/null
+++ b/aos/atom_code/async_action/AsyncActionHandle.h
@@ -0,0 +1,17 @@
+#ifndef __AOS__ASYNC_ACTION_HANDLE_H_
+#define __AOS__ASYNC_ACTION_HANDLE_H_
+
+namespace aos {
+
+	class AsyncActionHandle {
+		public:
+			virtual bool IsDone() = 0;
+			virtual uint16_t Join() = 0;
+			virtual uint16_t Join(int32_t count) = 0;
+			virtual void Stop() = 0;
+			virtual void Stop(int32_t count) = 0;
+	};
+
+} // namespace aos
+
+#endif
diff --git a/aos/atom_code/async_action/AsyncActionRunner.h b/aos/atom_code/async_action/AsyncActionRunner.h
new file mode 100644
index 0000000..148d13a
--- /dev/null
+++ b/aos/atom_code/async_action/AsyncActionRunner.h
@@ -0,0 +1,15 @@
+#ifndef __AOS_ASYNC_ACTION_RUNNER_H_
+#define __AOS_ASYNC_ACTION_RUNNER_H_
+
+#include "aos/atom_code/async_action/AsyncAction.h"
+
+namespace aos {
+	class AsyncActionRunner {
+		public:
+			template<class T, class S> inline static int Run(AsyncAction<T, S> &action, uint8_t priority) {
+				return action.Run(priority);
+			}
+	};
+}
+
+#endif
diff --git a/aos/atom_code/async_action/AsyncAction_real.cpp b/aos/atom_code/async_action/AsyncAction_real.cpp
new file mode 100644
index 0000000..9a0be1d
--- /dev/null
+++ b/aos/atom_code/async_action/AsyncAction_real.cpp
@@ -0,0 +1,10 @@
+#include "AsyncAction.h"
+
+using namespace aos;
+
+volatile uint8_t AsyncActionStatics::interrupt = 0;
+
+const int AsyncActionStatics::STOP_SIGNAL = SIGRTMIN + 4;
+
+aos_resource_entity *AsyncActionStatics::resource_entity = NULL;
+
diff --git a/aos/atom_code/async_action/AsyncAction_test.cpp b/aos/atom_code/async_action/AsyncAction_test.cpp
new file mode 100644
index 0000000..bdd4002
--- /dev/null
+++ b/aos/atom_code/async_action/AsyncAction_test.cpp
@@ -0,0 +1,115 @@
+#include "TestAction.h"
+#include "ResourceAction.h"
+extern "C"{
+#include <resource_internal.h>
+}
+
+#include <gtest/gtest.h>
+
+#include "sharedmem_test_setup.h"
+
+using namespace aos;
+
+class AsyncActionTest : public ExecVeTestSetup{
+	protected:
+		virtual void SetUp(){
+			AddProcess("../bin/TestAction");
+			AddProcess("../bin/ResourceAction");
+			//AddProcess("/home/brians/bin/wait_5s.sh");
+			AddProcess("../bin/ResourceActionChild");
+
+			ExecVeTestSetup::SetUp();
+		}
+
+		template<class H, class S, class T> bool HasResource(const uint16_t resource, H &handle){
+			/*bool r = AOS_RESOURCE_STATE_GET_HAS_IT(resource, handle.GetInstance().resource_entity);
+			EXPECT_EQ(r, handle.GetInstance().resources.count(resource)) << "the AsyncAction doesn't know whether it has resource " << resource << " or not";
+			return r;*/
+
+			/*const AsyncAction<S, T> &action = handle.GetInstance();
+			async_action_start<S> *start = (async_action_start<S> *)aos_queue_read_message(action.start_queue, PEEK | NON_BLOCK);
+			ASSERT_NE(NULL, start);
+			bool r = AOS_RESOURCE_STATE_GET_HAS_IT(resource, start->parent);
+			aos_queue_free_msg(action.start_queue, start);
+			return r;*/
+
+			AsyncAction<S, T> &action = (AsyncAction<S, T> &)handle.GetInstance();
+			async_action_status<T> *status = (async_action_status<T> *)aos_queue_read_msg(action.status_queue, PEEK | NON_BLOCK);
+			EXPECT_TRUE(status != NULL) << "if this failed, we're going to segfault next";
+			bool r = AOS_RESOURCE_STATE_GET_HAS_IT(resource, status->resource_entity);
+			aos_queue_free_msg(action.status_queue, status);
+			return r;
+		}
+
+		// tests from the google doc (https://docs.google.com/document/d/1gzRrVcqL2X9VgNQUI5DrvLVVVziIH7c5ZerATVbiS7U/edit?hl=en_US) (referenced by the numbers in the drawing)
+		// up here so they can be called with multiple inputs and checked for correct outputs
+		// return = number of sub-action call it failed at (1 = top level one etc) (0 = succeeded)
+		int GoogleDocTest1(uint8_t in_state){
+			return ResourceAction.Execute(0x01, 0x01, 1, in_state).failed;
+		}
+
+		virtual void TearDown(){
+			TestAction.Free();
+			LeftDrive.Free();
+			ResourceAction.Free();
+			ResourceAction.Free();
+			
+			ExecVeTestSetup::TearDown();
+		}
+};
+
+TEST_F(AsyncActionTest, StartStop){
+	TestAction.Start(5, 3);
+	TestAction.Stop();
+	PercolatePause();
+	EXPECT_TRUE(TestAction.IsDone());
+}
+TEST_F(AsyncActionTest, AlternateName){
+	EXPECT_FALSE(LeftDrive.IsDone());
+}
+
+TEST_F(AsyncActionTest, Join){
+	TestAction.Start(0.1, 3);
+	EXPECT_FALSE(TestAction.IsDone());
+	TestAction.Join();
+	EXPECT_TRUE(TestAction.IsDone());
+}
+TEST_F(AsyncActionTest, JoinAgain){
+	uint16_t instance = TestAction.Start(0.1, 3);
+	TestAction.Join();
+	EXPECT_TRUE(TestAction.IsDone());
+	timespec ts;
+	clock_gettime(CLOCK_MONOTONIC, &ts);
+	TestAction.Join(instance);
+	timespec ts2;
+	clock_gettime(CLOCK_MONOTONIC, &ts2);
+	long diff = ts2.tv_nsec - ts.tv_nsec;
+	diff += (ts2.tv_sec - ts.tv_sec) * 1000000000;
+	EXPECT_LT(diff, 50000);
+}
+TEST_F(AsyncActionTest, Execute){
+	TestAction.Execute(0.06, 3);
+	EXPECT_TRUE(TestAction.IsDone());
+}
+
+TEST_F(AsyncActionTest, Release){
+	TestAction.Start(0.1, -4);
+	while(!TestAction.IsDone()){
+		if(TestAction.GetNextStatus())
+			EXPECT_EQ(TestAction.GetLastStatus().loops > 2, !(HasResource<TestActionHandle, testing_status, testing_args>(test_resource1, TestAction)));
+	}
+}
+TEST_F(AsyncActionTest, ReleaseBad){
+	TestAction.Start(-0.01, 5);
+}
+TEST_F(AsyncActionTest, ImplicitRelease){
+	TestAction.Execute(0.02, 4);
+	EXPECT_FALSE((HasResource<TestActionHandle, testing_status, testing_args>(test_resource1, TestAction)));
+}
+
+//TODO test killing or not based on priority (and inherited priority...)
+
+TEST_F(AsyncActionTest, GoogleDoc111){
+	EXPECT_EQ(3, GoogleDocTest1(3));
+}
+
diff --git a/aos/atom_code/async_action/ResourceAction.act b/aos/atom_code/async_action/ResourceAction.act
new file mode 100644
index 0000000..46e22fb
--- /dev/null
+++ b/aos/atom_code/async_action/ResourceAction.act
@@ -0,0 +1,13 @@
+args resource_args {
+	// 1 = request, 2 = release, 4 = child run, 8 = child request, 16 = child release, all others 0
+	uint8_t first;
+	uint8_t second;
+
+	int number; // top level is 1
+
+	uint8_t state_to_set; // first 2 bits are this one, next 2 bits are for parent
+};
+status resource_status {
+	int failed; // 0 means none have failed yet
+};
+priority 100;
diff --git a/aos/atom_code/async_action/ResourceActionChild.act b/aos/atom_code/async_action/ResourceActionChild.act
new file mode 100644
index 0000000..5e8226a
--- /dev/null
+++ b/aos/atom_code/async_action/ResourceActionChild.act
@@ -0,0 +1,8 @@
+args resource_child_args{
+	// 1 = request, 2 = release
+	uint8_t actions;
+}
+status resource_child_status{
+	bool success;
+}
+priority 70;
diff --git a/aos/atom_code/async_action/ResourceActionChild_t.cpp b/aos/atom_code/async_action/ResourceActionChild_t.cpp
new file mode 100644
index 0000000..c1a2601
--- /dev/null
+++ b/aos/atom_code/async_action/ResourceActionChild_t.cpp
@@ -0,0 +1,13 @@
+#include "ResourceActionChild.h"
+
+void ResourceActionChild_t::DoAction(uint8_t actions){
+	if(actions & 0x01)
+		if(TryRequestResource(test_resource1)){
+			PostStatus(false);
+			return;
+		}
+	if(actions & 0x02)
+		ReleaseResource(test_resource1);
+	PostStatus(true);
+}
+
diff --git a/aos/atom_code/async_action/ResourceAction_t.cpp b/aos/atom_code/async_action/ResourceAction_t.cpp
new file mode 100644
index 0000000..99d9c7e
--- /dev/null
+++ b/aos/atom_code/async_action/ResourceAction_t.cpp
@@ -0,0 +1,40 @@
+#define AOS_ResourceAction_t_HEADER_FRAG int DoSub(uint8_t slot, int in_number);
+
+#include "ResourceAction.h"
+#include "ResourceActionChild.h"
+extern "C"{
+#include <resource_internal.h>
+}
+
+int ResourceAction_t::DoSub(uint8_t slot, int in_number){
+	if(slot & 0x01)
+		if(TryRequestResource(test_resource1)){
+			PostStatus(in_number + 1);
+			return 0;
+		}
+	if(slot & 0x02)
+		ReleaseResource(test_resource1);
+	if(slot & 0x04){
+		if(!ResourceActionChild.Execute(slot << 3).success){
+			PostStatus(in_number + 2);
+			return 0;
+		}
+	}
+	return in_number + ((slot & (0x01 | 0x02)) ? 1 : 0) + ((slot & 0x04) ? 1 : 0);
+}
+void ResourceAction_t::DoAction(uint8_t first, uint8_t second, int number, uint8_t state_to_set){
+	printf("start of ResourceAction.DoAction\n");
+	AOS_RESOURCE_STATE_SET_ON((AOS_RESOURCE_STATE_WANTS_IT | AOS_RESOURCE_STATE_HAS_PASSED_DOWN) & state_to_set, test_resource1, ::AsyncActionStatics::resource_entity);
+	AOS_RESOURCE_STATE_SET_OFF((AOS_RESOURCE_STATE_WANTS_IT | AOS_RESOURCE_STATE_HAS_PASSED_DOWN) & state_to_set, test_resource1, ::AsyncActionStatics::resource_entity);
+	AOS_RESOURCE_STATE_SET_ON((AOS_RESOURCE_STATE_WANTS_IT | AOS_RESOURCE_STATE_HAS_PASSED_DOWN) & (state_to_set >> 2), test_resource1, aos_resource_entity_root_get());
+	AOS_RESOURCE_STATE_SET_OFF((AOS_RESOURCE_STATE_WANTS_IT | AOS_RESOURCE_STATE_HAS_PASSED_DOWN) & state_to_set >> 2, test_resource1, aos_resource_entity_root_get());
+	printf("set state\n");
+
+	number = DoSub(first, number);
+	printf("did first\n");
+	if(number == 0) // error
+		return;
+	number = DoSub(second, number);
+	printf("did second\n");
+}
+
diff --git a/aos/atom_code/async_action/TestAction.act b/aos/atom_code/async_action/TestAction.act
new file mode 100644
index 0000000..96c940b
--- /dev/null
+++ b/aos/atom_code/async_action/TestAction.act
@@ -0,0 +1,13 @@
+args testing_args {
+	double seconds;
+	int loops;
+};
+status testing_status {
+	int loops;
+	double total_seconds;
+};
+async_queue LeftDrive;
+async_queue TestAction;
+//has OnEnd;
+//has OnStart;
+priority 50;
diff --git a/aos/atom_code/async_action/TestAction_t.cpp b/aos/atom_code/async_action/TestAction_t.cpp
new file mode 100644
index 0000000..1e99529
--- /dev/null
+++ b/aos/atom_code/async_action/TestAction_t.cpp
@@ -0,0 +1,35 @@
+#include "TestAction.h"
+#include <AsyncActionRunner.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+//void TestingAction_t::OnStart() {printf("started\n");}
+//void TestingAction_t::OnEnd() {printf("ended\n");}
+void TestAction_t::DoAction(double seconds, int loops) {
+	printf("start of DoAction\n");
+	RequestResource(test_resource1);
+	bool release = loops < 0;
+	if(release)
+		loops *= -1;
+	bool release_bad = seconds < 0;
+	if(release_bad)
+		seconds *= -1;
+	int i;
+	for(i = 0; i < loops; ++i) {
+		Sleep(seconds);
+		printf("posting for loop %d\n", i);
+		PostStatus(i, seconds * i);
+		printf("posted for loop %d\n", i);
+		if(release && i > loops / 2){
+			printf("releasing resource\n");
+			ReleaseResource(test_resource1);
+		}
+		if(release_bad && i > loops / 2){
+			printf("releasing resource which has not been requested\n");
+			ReleaseResource(test_resource2);
+		}
+	}
+	printf("end of DoAction\n");
+}
+
diff --git a/aos/atom_code/async_action/async_action.gyp b/aos/atom_code/async_action/async_action.gyp
new file mode 100644
index 0000000..5f5bf55
--- /dev/null
+++ b/aos/atom_code/async_action/async_action.gyp
@@ -0,0 +1,13 @@
+{
+  'targets': [
+    {
+      'target_name': 'AsyncAction_test',
+      'type': 'executable',
+      'sources': [
+        'AsyncACtion_test.cpp',
+      ],
+      'dependencies': [
+        '<(EXTERNALS):gtest',
+      ],
+  ],
+},