C API - Are callbacks done synchronously or asynchronously?

When calling callback functions, does libcouchbase call them synchronously one after the other, or can it call several at the same time on different threads? I’m curious because I want to know if I need mutexes when accessing shared resources in the callbacks.

In this case callbacks will be called in a sequence because we aren’t using threads in the library. Therefore we cannot execute the callbacks, bound to the same lcb_t handle, simultaneously.
But if you are going to run event loop (with the my_get_callback()) in one thread, but schedule the GET operations from the another thread (calling lcb_get(…)), you will get issues. Because on lcb_get() you are writing to the internal buffer, and after calling the appropriate callback it will flush the buffer.

I’m not totally clear on the answer, maybe because I’m not familiar with libev or libevent. I looked up the reactor pattern and it sounds like the callbacks would be done synchronously. Essentially, I want to make sure this kind of thing won’t give me problems in the default implementation:

MySharedObject my_shared_object;
my_get_callback(lcb_t instance, lcb_error_t error, …)
// If I get 2 keys, can libcouchbase call both
// callbacks at the same time? If so, I need a
// mutex for this line.

Libcouchbase doesn’t to any locks internally and doesn’t use mutexes. It is safe while you are not sharing your lcb_t handle between threads. If your application is multithreaded, you should implement some pooling when you will checkout the handle from the pool, and return it back after working with it exclusively. I think that you will get the best performance and maintainability if you will implement pool of connections instead of sharing single connection between threads.
The IO under the hoods is asynchronous and it gives you asynchronous interface using reactor pattern, which you can observe in different applications using libevent, libev, libuv and libraries like that. Generally it allows you maintain several active sockets in the single thread. Currently libcouchbase bundled with two IO plugins: libev and libevent, but also you can write your own. For example, node.js client uses its own IO plugin and provides routines to libcouchbase to perform network operations, ruby client ships even two libraries: one for EventMachine integration and another one specially to release ruby GVL on IO operations.