Repository: Xudong-Huang/may (language: rust)

Issue:
support set stack size and task name for go! macro
Example

```rust
go!(|| {
    println!("task with default stack size");
});

go!(|| {
    println!("task with specific stack size");
},
4096);

go!(|| {
    println!("task with specific stack size and name");
},
4096, String::from("task3"));
```


Discussion / hints:
`go!` macro already has three branches, it's not a good choice to add more branches. I think we can introduce a new macro such as `go_with!` may be a better choice:

```rust
go_with!(4096, || {
    println!("task with default stack size");
});
go_with!(4096,"task", || {
    println!("task with default stack size");
});

```

If you agree, i can create a PR.

Thanks! This is good. Let's first add a support for normal coroutine creation.

Fix patch:
diff --git a/src/coroutine_impl.rs b/src/coroutine_impl.rs
index 300db93d..82f25cd6 100644
--- a/src/coroutine_impl.rs
+++ b/src/coroutine_impl.rs
@@ -104,6 +104,7 @@ fn get_co_local(co: &CoroutineImpl) -> *mut CoroutineLocal {
 /// The internal representation of a `Coroutine` handle
 struct Inner {
     name: Option<String>,
+    stack_size: usize,
     park: Park,
     cancel: Cancel,
 }
@@ -118,16 +119,22 @@ unsafe impl Send for Coroutine {}
 
 impl Coroutine {
     // Used only internally to construct a coroutine object without spawning
-    fn new(name: Option<String>) -> Coroutine {
+    fn new(name: Option<String>, stack_size: usize) -> Coroutine {
         Coroutine {
             inner: Arc::new(Inner {
                 name,
+                stack_size,
                 park: Park::new(),
                 cancel: Cancel::new(),
             }),
         }
     }
 
+    /// Gets the coroutine stack size.
+    pub fn stack_size(&self) -> usize {
+        self.inner.stack_size
+    }
+
     /// Atomically makes the handle's token available if it is not already.
     pub fn unpark(&self) {
         self.inner.park.unpark();
@@ -272,7 +279,7 @@ impl Builder {
             co = Gn::new_opt(stack_size, closure);
         }
 
-        let handle = Coroutine::new(name);
+        let handle = Coroutine::new(name, stack_size);
         // create the local storage
         let local = CoroutineLocal::new(handle.clone(), join.clone());
         // attache the local storage to the coroutine
diff --git a/src/io/sys/unix/epoll.rs b/src/io/sys/unix/epoll.rs
index ca533567..2d6b6ca1 100644
--- a/src/io/sys/unix/epoll.rs
+++ b/src/io/sys/unix/epoll.rs
@@ -156,7 +156,10 @@ impl Selector {
     #[inline]
     pub fn add_fd(&self, io_data: IoData) -> io::Result<IoData> {
         let mut info = EpollEvent::new(
-            EpollFlags::EPOLLIN | EpollFlags::EPOLLOUT | EpollFlags::EPOLLRDHUP | EpollFlags::EPOLLET,
+            EpollFlags::EPOLLIN
+                | EpollFlags::EPOLLOUT
+                | EpollFlags::EPOLLRDHUP
+                | EpollFlags::EPOLLET,
             io_data.as_ref() as *const _ as _,
         );
 
diff --git a/src/macros.rs b/src/macros.rs
index 63f59328..3e2a336a 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -46,6 +46,45 @@ macro_rules! go {
     }};
 }
 
+/// macro used to spawn a coroutine with options such as name, stack_size.
+///
+/// this macro is just a convenient wrapper for [`spawn`].
+/// However the supplied coroutine block is not wrapped in `unsafe` block
+///
+/// [`spawn`]: coroutine/fn.spawn.html
+#[macro_export]
+macro_rules! go_with {
+    // for stack_size
+    ($stack_size:expr, $func:expr) => {{
+        fn _go_check<F, T>(stack_size: usize, f: F) -> F
+        where
+            F: FnOnce() -> T + Send + 'static,
+            T: Send + 'static,
+        {
+            f
+        }
+        let f = _go_check($stack_size, $func);
+        let builder = $crate::coroutine::Builder::new().stack_size($stack_size);
+        unsafe { builder.spawn(f) }
+    }};
+
+    // for name and stack_size
+    ($name: expr, $stack_size:expr, $func:expr) => {{
+        fn _go_check<F, T>(name: &str, stack_size: usize, f: F) -> F
+        where
+            F: FnOnce() -> T + Send + 'static,
+            T: Send + 'static,
+        {
+            f
+        }
+        let f = _go_check($name, $stack_size, $func);
+        let builder = $crate::coroutine::Builder::new()
+            .name($name.to_owned())
+            .stack_size($stack_size);
+        unsafe { builder.spawn(f) }
+    }};
+}
+
 /// macro used to create the select coroutine
 /// that will run in a infinite loop, and generate
 /// as many events as possible


Test patch:
diff --git a/tests/lib.rs b/tests/lib.rs
index 3019e025..05a9c40c 100644
--- a/tests/lib.rs
+++ b/tests/lib.rs
@@ -310,3 +310,40 @@ fn join_macro() {
     assert_eq!(rx1.try_recv().is_err(), true);
     assert_eq!(rx2.try_recv().is_err(), true);
 }
+
+#[test]
+fn go_with_macro() {
+    use may::sync::mpsc::channel;
+
+    let (tx1, rx1) = channel();
+    let (tx2, rx2) = channel();
+
+    go_with!(8192, move || {
+        tx1.send(coroutine::current().stack_size()).unwrap();
+    })
+    .unwrap()
+    .join()
+    .unwrap();
+
+    go_with!("test_task", 10240, move || {
+        let task = coroutine::current();
+        let msg = (
+            task.name().map(ToOwned::to_owned),
+            task.stack_size(),
+        );
+        tx2.send(msg).unwrap();
+    })
+    .unwrap()
+    .join()
+    .unwrap();
+
+    {
+        let x = rx1.recv().unwrap();
+        assert_eq!(x, 8192);
+    }
+    {
+        let (name, stack_size) = rx2.recv().unwrap();
+        assert_eq!(name, Some("test_task".to_owned()));
+        assert_eq!(stack_size, 10240);
+    }
+}