Show HN: CCo – Go-style coroutines in C

gtutino2 pts0 comments

GitHub - gtutino/CCo: Go-style coroutines in C · GitHub

/" data-turbo-transient="true" />

Skip to content

Search or jump to...

Search code, repositories, users, issues, pull requests...

-->

Search

Clear

Search syntax tips

Provide feedback

--><br>We read every piece of feedback, and take your input very seriously.

Include my email address so I can be contacted

Cancel

Submit feedback

Saved searches

Use saved searches to filter your results more quickly

-->

Name

Query

To see all available qualifiers, see our documentation.

Cancel

Create saved search

Sign in

/;ref_cta:Sign up;ref_loc:header logged out"}"<br>Sign up

Appearance settings

Resetting focus

You signed in with another tab or window. Reload to refresh your session.<br>You signed out in another tab or window. Reload to refresh your session.<br>You switched accounts on another tab or window. Reload to refresh your session.

Dismiss alert

{{ message }}

gtutino

CCo

Public

Notifications<br>You must be signed in to change notification settings

Fork

Star

main

BranchesTags

Go to file

CodeOpen more actions menu

Folders and files<br>NameNameLast commit message<br>Last commit date<br>Latest commit

History<br>60 Commits<br>60 Commits

include

include

src

src

test

test

.gitignore

.gitignore

LICENSE

LICENSE

Makefile

Makefile

README.md

README.md

main.c

main.c

View all files

Repository files navigation

CCo - C Coroutines

Overview

CCo implements Go-style coroutines in C.

Coroutines can use buffered and unbuffered channels to communicate in a message-passing style.

The coroutines can run in a single thread or in multiple threads using a thread pool of fixed os threads.

The API (include/cco.h) has all the functions to handle coroutines and channels,

if you already know Go programming language then the functions in the API will be familiar to you.

This project was made as an assignment for the Languages for Concurrency and Distribution course of the University of Padua, it's written in C (C11) and has no external dependencies.

[NOTE] This is just a fun personal project, so I don't recommend using it in industrial projects.

Architecture

Each coroutine has a context that saves the register state, the executing stack and infos about its running state.<br>This coroutines are organied in a circular queue (using a doubly linked list) and the "scheduling" is just round robin, so when a coroutine yields the current thread just looks at the next node of the list and do the context switch.

The context switch it's implemented in asm with custom procedures.

Coroutine free is handled automatically by the coroutine itself. When a coroutine starts, the address of a cleanup function is put at the top of the stack, so when the coroutine returns from the last stack frame it will jump to the cleanup function.

Thread Pool

Coroutines can run with multiple threads in a thread pool way, by default thread pool has N threads where N is the number of cpu<br>cores. By the way, CCO_THREADS enviroment var can be set to a fixed amount of thread by the user.

Each thread has its own local coroutines organized as said before (circular queue) and cannot access to other threads queues. So there is no thread syncronization when accessing local coroutines.

Then there is a global queue that can be used to pull and push coroutines by all the threads, so its access is handled by a mutex.

A thread will push to the global queue if its local coroutines are more of a fixed threshold (based on the total number of coroutines) and pull in the opposite situation, in this way all the threads will have (approximately) the same amount of coroutines.

This behaviour happens every time there is a context switch, since there is no preemption if the coroutines don't switch frequently there can be some misalignments.

Termination is handled by just looking at the total number of coroutines, when it reaches 0 the program terminates.

Channels

Channels can be used by multiple coroutines without limits, so they are general MxN channels.

They can be buffered (with a fixed size) or unbuffered, so the interaction can also be async.

The order of messages is always preserved.

A channel structure is basicall composed of two queues (send and recv queue) and the message data is just copied to destination through memcpy. All channels operation are (obviously) locked because they can be accessed by multiple threads.

The mutex are of type PTHREAD_MUTEX_RECURSIVE, this is needed because the select statement locks prevently the available channels and then do send/recv on the selected one, locking two times the same mutex.

Limitations

Coroutines can take up to 6 args of maximum 8 bytes each.

Select function can take up to 16 channel actions.

The stack of each coroutine is fixed to a constant value.

The code in src doesn't compile with compiler optimization and needs -fno-omit-frame-pointer flag for ensuring correct behaviour.

There is no preemption, so if a coroutine doesn't use yield or...

coroutines thread threads coroutine channels search

Related Articles