Обратные вызовы C и не-потоки - PullRequest
10 голосов
/ 30 ноября 2010
  1. Как можно вызвать код Go в C из потоков, которые не были созданы Go?
  2. Что я назначаю указателю функции C, чтобы потоки, не созданные Go, могли вызвать этот указатель и ввести код Go?

Update0

  • Я не хочу использовать SWIG.
  • Обратные вызовы будут приходить из потоков, которые Go раньше не видел. Ни cgo/life, ни что-либо в pkg/runtime не демонстрируют такое поведение AFAICT.

Ответы [ 3 ]

5 голосов
/ 01 марта 2011

Вы можете сделать это, но решение является относительно медленным (около 22 мкс на вызов на моем компьютере). Ответ заключается в том, чтобы код C использовал примитивы потока C для связи с другой программой, которая фактически будет выполнять обратный вызов.

Я создал пакет Go, который обеспечивает эту функциональность: rog-go.googlecode.com / hg / exp / callback . Вот пример пакета, демонстрирующего его использование здесь . В этом примере демонстрируется обратный вызов произвольного замыкания Go из потока, созданного вне среды выполнения Go. Другой пример - здесь . Это демонстрирует типичный интерфейс обратного вызова C и накладывает поверх него обратный вызов Go.

Чтобы попробовать первый пример:

goinstall rog-go.googlecode.com/hg/exp/example/looper
cd $GOROOT/src/pkg/rog-go.googlecode.com/hg/exp/example/looper
gotest

Чтобы попробовать второй пример:

goinstall rog-go.googlecode.com/hg/exp/example/event
cd $GOROOT/src/pkg/rog-go.googlecode.com/hg/exp/example/event
gotest

В обоих примерах предполагается, что доступны pthreads. Конечно, это всего лишь мера остановки до тех пор, пока cgo не будет зафиксировано, но метод вызова произвольных замыканий Go в обратном вызове C будет применим даже тогда.

Вот документация для пакета обратного вызова:

ПАКЕТ

package callback
import "rog-go.googlecode.com/hg/exp/callback"

ПЕРЕМЕННЫЕ

var Func = callbackFunc

Func содержит указатель на функцию обратного вызова C. Когда вызвано, это вызывает предоставленную функцию f в контекст Go с заданным аргументом.

Его можно использовать, сначала преобразовав его в указатель на функцию а затем звонит из C. Вот пример, который устанавливает функцию обратного вызова:

//static void (*callback)(void (*f)(void*), void *arg);
//void setCallback(void *c){
//  callback = c;
//}
import "C"
import "rog-go.googlecode.com/hg/exp/callback"

func init() {
    C.setCallback(callback.Func)
}
4 голосов
/ 30 ноября 2010

Полагаю, вы имеете в виду код C, скомпилированный с помощью gcc?

IIRC, это либо невозможно, либо нелегко сделать, используя 6g + cgo и друзей. Go использует другое соглашение о вызовах (а также сегментированные стеки и т. Д.).

Однако вы можете написать C-код для [685] c (или даже [685] a) и легко вызвать go с помощью package · function () (вы даже можете вызвать методы IIRC). См. Источник пакета runtime для примеров.

Обновление:

Возвращаясь к этому вопросу после обновления, и немного подумав. Это нельзя сделать стандартным способом, используя 6c или cgo. Тем более, что потоки не запускаются во время выполнения go, текущая реализация не будет выполнена. У планировщика внезапно окажется под контролем поток, о котором он не знает; Кроме того, в этом потоке будут отсутствовать некоторые локальные переменные потока, используемые средой выполнения go для управления стеками и некоторыми другими вещами. Кроме того, если функция go возвращает значение (или несколько), код C не может получить к нему доступ на поддерживаемых в настоящее время платформах, так как go возвращает значения в стеке (хотя вы можете получить к ним доступ с помощью сборки). Имея это в виду, я верю, что вы все еще можете сделать это, используя каналы. Это потребовало бы, чтобы ваш код на C был слишком тесно связан с внутренней работой среды выполнения go, но это сработало бы для данной реализации. Хотя использование каналов может оказаться не тем решением, которое вам нужно, оно может лучше соответствовать концепциям Go, чем обратным вызовам. Если ваш код на C переопределил по крайней мере методы отправки в Реализация канала (этот код написан для 6c, поэтому он, скорее всего, должен быть адаптирован для gcc, и он вызывает среду выполнения go, которую мы ' Если вы решили, что это невозможно сделать из непроходного потока), вы должны иметь возможность заблокировать канал и передать ему значение. Планировщик go может продолжать управлять своими собственными потоками, но теперь он может получать данные из других потоков, запущенных в C.

Правда, это хак; Я не посмотрел достаточно близко, но, вероятно, потребовалось бы несколько других хаков, чтобы заставить его работать (я полагаю, что сами каналы поддерживают список ожидающих их программ [РЕДАКТИРОВАТЬ: подтверждено: runtime·ready(gp);], так что вы Вам нужно что-то в вашем коде go, чтобы разбудить принимающий канал или гарантировать, что код go не будет получен на канале, пока вы уже не введете значение). Однако я не вижу причин, по которым это не может работать, в то время как есть определенные причины, по которым выполнение кода, сгенерированного 6g, в потоке, созданном в C, не может.

Хотя мой первоначальный ответ остается в силе: без добавления языка или среды выполнения, это еще не может быть сделано так, как вы хотели бы (я бы хотел, чтобы вас здесь не подтвердили).

3 голосов
/ 03 апреля 2011

Вы можете найти реальное применение пакета обратного вызова rog в этих привязках для библиотеки ввода-вывода аудио PortAudio: http://code.google.com/p/portaudio-go/. Может облегчить понимание ..

(Спасибо за реализацию, Рог. Это как раз то, что мне нужно!)

...