Revisit the blocking behavior of `malloc` and `calloc`.

We have recently changed the blocking behavior of `malloc` and `calloc`
in 3ff0dda87b07973e588cd7efa522c7de6e7fe2ca from a fully blocking
behavior to an entirely non-blocking behavior.

This change, although it addressed some problems, created new ones: we
now have situations where malloc is returning `nullptr` because the
quarantine is holding back memory which could be used for the
allocation. In such cases, the allocation could be satistified with a
small wait. This creates issues in the network stack.

This indicates that neither fully blocking nor fully non-blocking is the
right approach here.

This commit proposes to immediately return `nullptr` if the heap or the
quota is full (do not block), but block for a short timeout if the
quarantine is holding back memory which could be used for the
allocation. The short timeout can be configured through a macro, here
set at 30 ticks which seems to be a reasonable time for the revoker to
process the quarantine.

Modify `MVM_CONTEXTUAL_MALLOC` to exhibit a similar behavior.

Signed-off-by: Hugo Lefeuvre <hugo.lefeuvre@scisemi.com>
diff --git a/sdk/include/microvium/microvium_port.h b/sdk/include/microvium/microvium_port.h
index 1b2e4b1..c18188a 100644
--- a/sdk/include/microvium/microvium_port.h
+++ b/sdk/include/microvium/microvium_port.h
@@ -295,11 +295,15 @@
  *
  * The `context` passed to these macros is whatever value that the host passes
  * to `mvm_restore`. It can be any value that fits in a pointer.
+ *
+ * Similarly to `malloc` and `calloc`, this will only ever
+ * block to wait for the quarantine to be processed.
  */
 #define MVM_CONTEXTUAL_MALLOC(size, context)                                   \
 	({                                                                         \
-		Timeout t   = {0, 0};                                                  \
-		void   *ret = heap_allocate(&t, context, size);                        \
+		Timeout t = {0, MALLOC_WAIT_TICKS};                                    \
+		void   *ret =                                                          \
+		  heap_allocate(&t, context, size, AllocateWaitRevocationNeeded);      \
 		if (!__builtin_cheri_tag_get(ret))                                     \
 		{                                                                      \
 			ret = NULL;                                                        \
diff --git a/sdk/include/stdlib.h b/sdk/include/stdlib.h
index de007dd..6955d23 100644
--- a/sdk/include/stdlib.h
+++ b/sdk/include/stdlib.h
@@ -85,6 +85,17 @@
  */
 #define MALLOC_CAPABILITY STATIC_SEALED_VALUE(__default_malloc_capability)
 
+#ifndef MALLOC_WAIT_TICKS
+/**
+ * Define how long a call to `malloc` and `calloc` can block to fulfil an
+ * allocation. Regardless of this value, `malloc` and `calloc` will only ever
+ * block to wait for the quarantine to be processed. This means that, even with
+ * a non-zero value of `MALLOC_WAIT_TICKS`, `malloc` would immediately return
+ * if the heap or the quota is exhausted.
+ */
+#	define MALLOC_WAIT_TICKS 30
+#endif
+
 __BEGIN_DECLS
 static inline void __dead2 panic()
 {
@@ -288,8 +299,9 @@
 #ifndef CHERIOT_NO_AMBIENT_MALLOC
 static inline void *malloc(size_t size)
 {
-	Timeout t   = {0, 0};
-	void   *ptr = heap_allocate(&t, MALLOC_CAPABILITY, size, AllocateWaitNone);
+	Timeout t = {0, MALLOC_WAIT_TICKS};
+	void   *ptr =
+	  heap_allocate(&t, MALLOC_CAPABILITY, size, AllocateWaitRevocationNeeded);
 	if (!__builtin_cheri_tag_get(ptr))
 	{
 		ptr = NULL;
@@ -298,9 +310,9 @@
 }
 static inline void *calloc(size_t nmemb, size_t size)
 {
-	Timeout t = {0, 0};
-	void   *ptr =
-	  heap_allocate_array(&t, MALLOC_CAPABILITY, nmemb, size, AllocateWaitNone);
+	Timeout t   = {0, MALLOC_WAIT_TICKS};
+	void   *ptr = heap_allocate_array(
+	    &t, MALLOC_CAPABILITY, nmemb, size, AllocateWaitRevocationNeeded);
 	if (!__builtin_cheri_tag_get(ptr))
 	{
 		ptr = NULL;