Best regards,
Twister
While I haven't taken the time to understand your situation in detail,
it seems as though it'll admit expression in pure Tcl either with
[after], or perhaps Miguel's new co-routines. That's where *I* would
start looking for a solution.
Yes, but since you never replied to:
I for one cannot help further.
-Alex
That's true, but I realized my need a C funciotn pointer wasn't so
desirable. Sorry for that and thanks for try to give me a hand.
@Cameron Laird: "After" command is not an answer here because the
callback trigger source is out of the program control. This function
should be executed asynchronously and I cannot determine the time when
it will happen.
The other thing: why the procedure which is to be called on the C side
should not have "puts" commands inside? Besides it works even with
"puts" but only within function registered with Tcl_Init function.
You're still not answering. That's too bad, because I suspect (and
Cameron too it seems) that we'd be able to suggest a much simpler
solution than the Tcl-C-Tcl nightmare you are diving into.
-Alex
Ok, I didn't realize previous things matters. So this is my idea:
I wanted to create an extention from my library which manages an
external device. The device is able to aquire or generate some
signals. For both operations usually it is necessary to register a
callback function you can read/write the data to be aquired/generated.
I have made such callback in C but since the main control is from Tcl
side I have to invoke tcl procedure (from the callback body) which
allows me to read or update the data buffers.
I didn't insist on using C-like-pointers, but now when I have such
implementation it is the easiest thing to invoke the script, I
guess...
Ok. Funnily this situation is one I've encountered dozens of time at
work.
The real winner here is to avoid premature optimization. This means:
start with a looser coupling, with a child process in C doing the
device interaction, and I/O for data and control between Tcl and the
child. That's the Tcl-as-glue silver bullet. Then do some profiling.
Then, *if* profiling shows that context switches are killing you, but
only in that case, start thinking about tighter couplings, like
extensions.
But maybe you've already conducted this analysis. Just tell me.
-Alex
> Then, *if* profiling shows that context switches are killing you, but
> only in that case, start thinking about tighter couplings, like
> extensions.
>
> But maybe you've already conducted this analysis. Just tell me.
>
> -Alex
Hi Alex,
Unfortunately there is no other way now to solve the problem than
extention, because the time is high and I cannot spend too long
searching for a new possibility. All I did work very well except the
one with callbacks using "stdout" channel. Do you have any idea how to
work this out.
The funniest thing "stdout" is reachable within registered C function.
But causes an error when you want to invoke it from other place.
Twister
Perhaps we should explain the benefits: with this C-coded stand-alone
executable we're recommending, development and testing of the hardware-
specific aspects does NOT depend on Tcl expertise. There are no
particular issues of synchronicity or thread of control or who owns
which stdio handle. Test the serial-line management in isolation.
With known-good behavior in that domain, manage the behavior with a
Tcl wrapper. There is no complex thinking about callbacks and control
sequences and such.
> Perhaps we should explain the benefits
Thanks for giving me number of advantages for creating executable
instead of C Api extension, but it still doesn't solve my problem why
I can't "Eval" some procedures from C code.
Thanks in advance.
Twister
You are trying to call Tcl_Eval from within the callback function? That
almost never works. Often, it's not allowed to call the C library from
within the signal handler. The only safe pssobility is
1) Use Tcl_AsyncMark in the callback handler, which is thread safe
2) In the Tcl_AsyncProc, fire an event
3) React to that event from Tcl
Christian
In my experience this reasoning has always proven wrong. You're
overestimating the cost of the switch *and* you're underestimating the
brittleness of what you're sticking to. Post a summary of your
extension's API (just the added Tcl commands) and we'll give you the
skeleton of the standalone main().
-Alex
Now I am very confused. I am aware of the risk of asynchronous "eval",
but from the other side why Tcl_Eval (Tcl_EvalObjv is probably the
same) wouldn't be asynchronous-protected. That's the purpose of using
Tcl_Eval in my opinion. Maybe you can show me the larger description
of Tcl_Eval-like procedures then man pages?
To be honest posting the interface functions may take lots of space
here and it won't probably show the heart of the problem. This is
because the "callback handler" (or everything that is needed to create
one) is stored in dynamic object which controls the data exchange
program system and external device. But if you need only added
commands body I will post it. I just ask to make sure what is
neccessary for you exactly. Some examples would also be very helpful.
The next issue: when the extension is exchanged with standalone
executable how it will communicate with the other part of the program?
Pipes? Sugnals?
It seems I have to learn more than more to understand well all the
integration issues. Will you guide me?
Twister
OK here is the sketch:
int main(int argc,char **argv)
{
InitWhateverNeedsBe();
while(fgets(line,SIZE,stdin))
{
if (2==sscanf(line,"foo %d %d",&x,&y)) res=Foo(x,y);
elseif (2==sscanf(line,"bar %s",a)) res=Bar(a);
...
else {printf("SYNTAX\n");fflush(stdout);continue;}
printf(res?"ERROR\n":"OK\n");fflush(stdout);
}
}
Here, InitWhateverNeedsBe, Foo, and Bar are your domain-dependent
synchronous functions.
So basically your standalone program is a kind of "shell" wrapped
around your device calls.
Presumably one of their side-effects would have started some thread or
registered some signal handler, so that callbacks can be called while
blocking on the fgets() or inside the execution of a Foo(). For these
callbacks to have a useful effect, one can imagine them doing
something like
sprintf(buf,"EVT: foo bar baz\n");
write(1,buf,strlen(buf));
(here I'm assuming signal handler context which precludes the use of
the non-reentrant parts of stdio like fprintf or fwrite)
> The next issue: when the extension is exchanged with standalone
> executable how it will communicate with the other part of the program?
> Pipes? Sugnals?
Pipes. On the Tcl side you open a bidirectional pipe (stored in a
single channel):
set pi [open "|mymain" r+]
fconfigure $pi -buffering line
When you need to issue a synchronous command:
proc Foo {x y} {
puts $::pi "Foo $x $y"
while {1} {
if {[gets $::pi line]<0} {fatal "Unexpected end of
mymain !!!"}
switch -glob -- $line {
OK* return
ERROR* {error "Oops in Foo"}
EVT* {evtcallback $line}
}
}
}
But events can also occur in between sync commands:
proc gotevent {} {
if {[gets $::pi line]<0} {fatal "Unexpected end of
mymain !!!"}
switch -glob -- $line {
EVT* {evtcallback $line}
* {fatal "Loss of sync: '$line' outside commands"}
}
}
fileevent $::pi readable gotevent
...
vwait forever
To summarize:
- the main() is a simple shell, with blocking, acknowledged commands
(OK/ERROR) and async events (EVT), all three being lines on its stdout
- the Tcl script spawns it between pipes
- synchronous commands are just a pair of I/O
- asynchronous events get picked up by [fileevent] in the Tcl event
loop
Benefits:
- the shell itself is unit-testable without Tcl (huge bonus, believe
me)
- your Tcl callbacks are called by a proven Tcl mechanism, no
Panics, no segfaults.
Drawbacks:
- you may be wasting a few microseconds of CPU per call/event.
> It seems I have to learn more than more to understand well all the
> integration issues. Will you guide me?
Yes, that's the deal :)
Tell me if the above still holds dark areas.
-Alex