Общая переменная между streamами Tcl и C

У меня есть основная программа Tcl, и я хочу создать из нее stream C. Затем мне нужно будет обмениваться информацией между двумя streamами: процесс streamа C часто обновляет входы / выходы. Я вижу два возможных решения моей проблемы: (1) переменная streamа Tcl порта Tcl для C, но я не видел никакой информации об этом в TCL-C API. (2) Создайте связанные с Tcl-C переменные и используйте их в качестве аргументов при создании streamа C. Последняя идея, похоже, не работает. Вот код C:

#include  /* startRoutine */ static void startRoutine (ClientData clientData) { int *Var; Var= (int *) clientData; int locA=0; int j; int k; while (1) { if (locA=!*Var) { // Modify Tcl-C shared variable locA=2 * *Var; *Var=locA; for (j=0; j<100; j++){} } else { for (k=0; k<100; k++){} } } } static int createThreadC_Cmd( ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { // Contains the ID of the newly created thread Tcl_ThreadId id; // Thread argument ClientData limitData; // Transfering global var argument to the created thread limitData=cdata; // Thread creation id=0; Tcl_CreateThread(&id, startRoutine, limitData, TCL_THREAD_STACK_DEFAULT, TCL_THREAD_NOFLAGS); // Wait thread process, before returning to TCL prog int i; int aa; for (i=0 ; i<10000000 ; i++){ aa=i; } // Return thread ID to tcl prog to allow mutex use Tcl_SetObjResult(interp, Tcl_NewIntObj((int) id)); return TCL_OK; } int DLLEXPORT Behavcextension_Init(Tcl_Interp *interp) { if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) { return TCL_ERROR; } // Create global Var int *sharedPtr; int linkedVar=0; sharedPtr=&linkedVar; Tcl_LinkVar(interp, "linkedVar", (char *) sharedPtr, TCL_LINK_INT); Tcl_CreateObjCommand(interp, "createThreadC", createThreadC_Cmd, sharedPtr, NULL); return TCL_OK; } 

Вот код Tcl:

 # linkedVar initial value in Tcl, will be overwritten by C Tcl_LinkVar() function set linkedVar 98 puts "linkedVar: $linkedVar" # Thread creation #------------------ load [file join [pwd] libBehavCextension[info sharedlibextension]] set threadId [createThreadC] puts "Created thread $threadId, waiting" # When Tcl_LinkVar() is called, initiate linkedVar at 2 puts "linkedVar: $linkedVar" # Function inside thread should modify linkedVar into linkedVar*2 set linkedVar 98 after 5000 puts "linkedVar: $linkedVar" 

Выход терминала находится здесь:

 Main thread ID: tid0xb779b6c0 linkedVar: 98 Created thread -1227252928, waiting linkedVar: 2 linkedVar: 98 

Последний результат должен быть 2 * 98 = 196. Создание LinkVar между Tcl и C является Ok (мы получаем 2 после создания ссылки), но передача LinkVar в Thread – это KO. Любое решение или объяснения о том, почему он не работает / что делать, чтобы его разрешить, приветствуются!

    Проблема остается такой же, как и в другом вопросе. Вы выделяете хранилище для переменной на стороне C в стеке C в функции, которая заканчивается вскоре после этого. Неопределенное поведение ссылается на эту переменную (которая linkedVar в Behavcextension_Init ) после завершения функции ( Behavcextension_Init ). Фактически происходит то, что фактическое хранилище используется для какого-то другого вызова функции (кто знает, что), и поэтому содержащееся значение произвольно, а его изменение может привести к «захватывающему» поведению.

    Вы хотите иметь переменную, которая существует после завершения Behavcextension_Init , поэтому она не должна выделяться в стеке этой функции. Самый простой способ заключается в следующем:

     int DLLEXPORT Behavcextension_Init(Tcl_Interp *interp) { int *sharedPtr; if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) { return TCL_ERROR; } sharedPtr = (int *) Tcl_Alloc(sizeof(int)); // Allocate *sharedPtr = 0; // Initialize Tcl_LinkVar(interp, "linkedVar", (char *) sharedPtr, TCL_LINK_INT); Tcl_CreateObjCommand(interp, "createThreadC", createThreadC_Cmd, sharedPtr, NULL); return TCL_OK; } 

    Предостережения

    1. Это утечка памяти, так как для Tcl_Free нет соответствующего Tcl_Alloc . Для памяти, выделенной один раз на процесс, это не большая проблема. В конце концов, это всего лишь несколько байт, и ОС вернет его при выходе.
    2. Это небезопасно при чтении переменной из другого streamа, кроме той, где она была написана; просто нет гарантии, что он сработает. Это, вероятно, будет работать, поскольку это всего лишь целое число, но вы зависите от аппаратного обеспечения, которое будет работать. Правильная вещь – выделить структуру, содержащую как переменную, так и подходящий мьютекс, и защитить доступ к переменной (независимо от того, читает или записывает) с мьютексом. Это, в свою очередь, требует, чтобы вы не использовали Tcl_LinkVar – он ничего не знает о памяти, защищенной мьютексом, но Tcl_LinkVar – это всего лишь shell Tcl_TraceVar которая обеспечивает обратный вызов, который выполняет связь между переменной Tcl (см. Tcl_GetVar и Tcl_SetVar ) и переменной C; написав свой собственный, который знает, как сделать обработку защиты от мутекса, не сложно. (Если вам интересно, получите источник в Tcl_LinkVar и адаптируйте его самостоятельно, он не использует какие-либо частные вызовы API.)