1
diff -ruN cwdaemon-0.7/Makefile.am cwdaemon-0.7-zia/Makefile.am
2
--- cwdaemon-0.7/Makefile.am 2003-05-14 19:45:57.000000000 +0200
3
+++ cwdaemon-0.7-zia/Makefile.am 2003-11-24 17:39:48.000000000 +0100
8
-cwdaemon_SOURCES = cwdaemon.c cwdaemon.h lp.c ttys.c
9
+cwdaemon_SOURCES = cwdaemon.c cwdaemon.h lp.c select.c ttys.c
13
diff -ruN cwdaemon-0.7/cwdaemon.c cwdaemon-0.7-zia/cwdaemon.c
14
--- cwdaemon-0.7/cwdaemon.c 2003-10-26 20:17:40.000000000 +0100
15
+++ cwdaemon-0.7-zia/cwdaemon.c 2003-11-25 12:35:50.000000000 +0100
21
+int sin_len, reply_socklen;
22
int socket_descriptor;
23
-struct sockaddr_in k_sin;
24
+struct sockaddr_in k_sin, reply_sin;
26
+char reply_data[256];
30
int morse_gap = 0; /* TODO: Set with -d delay */
32
long int dot = 1000000; /* dot length = unit */
33
long int dash = 3000000; /* dash length = 3-dot */
34
long int eldelay = 1000000; /* pause between elements equals to dot time */
35
-unsigned int ptt_delay = 0; /* default = off */
37
-/* various variables */
38
-int ptt_timer_running = 0;
39
+unsigned int ptt_delay = 1000000; /* default = off */
40
unsigned int bandswitch;
42
struct timeval now, end, left;
47
+/* WARNING! Character '^' is used for command 'e' */
48
morse morsetable[] = {
49
{' ', {NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL}},
50
{'A', {DIH, DAH, NIL, NIL, NIL, NIL, NIL, NIL}},
52
cwdevice *cwdev = &cwdevice_ttys;
55
+static morse *morse_playing=NULL;
56
+static int morse_i=0;
59
int weight [-5...0...+5]
60
0 = dot/space ratio 1:3, element space = dot time
66
- this is sample code shameless :-) stolen from
67
- http://www.erlenstar.demon.co.uk/unix/faq_8.html
70
-/* some simple timing utilities */
73
-timer_add (struct timeval *tv, long secs, long usecs)
76
- if ((tv->tv_usec += usecs) >= 1000000)
78
- tv->tv_sec += tv->tv_usec / 1000000;
79
- tv->tv_usec %= 1000000;
83
-/* Set *RES = *A - *B, returning the sign of the result */
86
-timer_sub (struct timeval *res, const struct timeval *a,
87
- const struct timeval *b)
89
- long sec = a->tv_sec - b->tv_sec;
90
- long usec = a->tv_usec - b->tv_usec;
93
- usec += 1000000, --sec;
96
- res->tv_usec = usec;
98
- return (sec < 0) ? (-1) : ((sec == 0 && usec == 0) ? 0 : 1);
103
set_switch (unsigned int bandswitch) // band switch function
105
debug ("Band switch output not implemented");
117
- cwdev->ptt (cwdev, ON);
118
- debug ("PTT (TUNE) on");
120
- udelay (ptt_delay);
122
- cwdev->cw (cwdev, ON);
123
- debug ("CW (TUNE) on");
124
- /* sidetone on if morsetone !=0 */
125
- fd = open ("/dev/tty1", O_WRONLY);
128
- if (ioctl (fd, KIOCSOUND, morse_tone) != 0)
130
- errmsg ("Ioctl console speaker");
137
- errmsg ("Open console speaker");
145
- gettimeofday (&end, NULL);
146
- timer_add (&end, 0, dash / morse_speed);
147
- ptt_timer_running = 1;
149
- cwdev->cw (cwdev, OFF);
150
- debug ("CW (TUNE) off");
151
- fd = open ("/dev/tty1", O_WRONLY);
154
- if (ioctl (fd, KIOCSOUND, 0) != 0)
156
- errmsg ("Ioctl console speaker");
163
- errmsg ("Open console speaker");
166
- /* delayed ptt off */
167
- if (1 == ptt_timer_running)
171
- udelay (1000); /*prevent 100% CPU */
172
- gettimeofday (&now, NULL);
173
- if (timer_sub (&left, &end, &now) <= 0)
175
- cwdev->ptt (cwdev, OFF);
176
- debug ("PTT (TUNE) off");
177
- ptt_timer_running = 0;
185
/* convert character to morse by searching the morsetable */
187
@@ -459,95 +350,130 @@
191
-/* output to console and serial/parallel port */
193
-playbeep (long duration)
196
+void sound(int tone)
201
+ fd = open ("/dev/tty1", O_WRONLY);
204
+ errmsg ("Open console speaker");
208
+ if (ioctl (fd, KIOCSOUND, tone) != 0)
210
- signal (SIGINT, SIG_IGN);
211
- signal (SIGQUIT, SIG_IGN);
212
- fd = open ("/dev/tty1", O_WRONLY);
215
- if (ioctl (fd, KIOCSOUND, morse_tone) != 0)
217
- errmsg ("Ioctl console speaker");
224
- errmsg ("Open console speaker");
227
+ errmsg ("Ioctl console speaker");
233
- cwdev->cw (cwdev, ON);
234
- udelay (duration / morse_speed);
238
- fd = open ("/dev/tty1", O_WRONLY);
241
- if (ioctl (fd, KIOCSOUND, 0) != 0)
243
- errmsg ("Ioctl console speaker");
250
- errmsg ("Open console speaker");
254
- signal (SIGINT, SIG_DFL);
256
- signal (SIGINT, catchint);
257
- signal (SIGQUIT, SIG_DFL);
259
- cwdev->cw (cwdev, OFF);
262
-/* play dot, dash and use a delay */
264
-playmorse (morse * m)
265
+void playmorse (void *xxx)
268
+ if (!morse_playing) {
269
+ debug("playmorse: morse_playing= NULL");
270
+ last_id=install_timer(ptt_delay, offdelay, NULL); /* ptt delay can be 0 */
273
+ debug("playmorse morse_playing='%c' i=%d", morse_playing->code, morse_i);
275
+ switch(morse_playing->data[morse_i++]){
278
+ get_next_char(); /* NOT first character, we don't need ptt */
279
+ last_id=install_timer((dash + ((dot / 10) * morse_gap))/morse_speed, playmorse, NULL);
285
+ if (morse_sound) sound(morse_tone);
286
+ cwdev->cw (cwdev, ON);
287
+ last_id=install_timer (dot / morse_speed, playpause, NULL);
292
+ if (morse_sound) sound(morse_tone);
293
+ cwdev->cw (cwdev, OFF);
294
+ last_id=install_timer (dash / morse_speed, playpause, NULL);
299
+void playpause (void *xxx){
300
+ debug("playpause");
302
+ if (morse_sound) sound(0);
303
+ if (morse_playing) last_id=install_timer(eldelay/morse_speed, playmorse, NULL);
306
+void pttdelay (void *xxx){
308
+ if (ptt_delay) cwdev->ptt (cwdev, ON);
309
+ /* morse_playing and morse_i must be set before */
310
+ last_id=install_timer(0, playmorse, NULL);
313
+void offdelay (void *xxx){
315
+ if (ptt_delay) cwdev->ptt (cwdev, OFF);
319
+void tune(int onoff){
320
+ debug("tune(%d)", onoff);
322
+ if (ptt_delay) cwdev->ptt (cwdev, ON);
323
+ debug ("PTT (TUNE) on");
324
+ last_id=install_timer(ptt_delay, tune_pttdelay, NULL); /* ptt_delay can be 0 */
326
+ cwdev->cw (cwdev, OFF);
327
+ debug ("CW (TUNE) off");
330
+ last_id=install_timer(dash/morse_speed, tune_offdelay, NULL);
332
+ last_id=install_timer(0, tune_offdelay, NULL);
336
+void tune_pttdelay (void *xxx){
337
+ debug("tune_pttdelay");
338
+ cwdev->cw (cwdev, ON);
339
+ debug ("CW (TUNE) on");
340
+ if (morse_sound) sound(morse_tone);
343
- while (d < 8 && m->data[d] != NIL)
345
- if (m->data[d] == DIH)
347
- if (m->data[d] == DAH)
350
- udelay (eldelay / morse_speed); /* element spacing */
351
- } /* morse signs delay */
352
- udelay ((dash + ((dot / 10) * morse_gap)) / morse_speed);
353
- morse_gap = 0; /* gap for one sign only */
355
+void tune_offdelay (void *xxx){
356
+ debug("tune_offdelay");
357
+ if (ptt_delay) cwdev->ptt (cwdev, OFF);
358
+ debug ("PTT (TUNE) off");
364
* watch the socket and if there is an escape character check what it is,
365
* otherwise play morse. Return 0 with escape characters and empty messages.
369
+void recv_code (void *xxx)
372
+ char message1[257];
380
- recvfrom (socket_descriptor, message, sizeof (message) - 1, 0,
381
+ recvfrom (socket_descriptor, message1, sizeof (message1) - 1, 0,
382
(struct sockaddr *) &k_sin, &sin_len);
384
if (recv_rc == -1 && errno != EAGAIN)
385
@@ -555,16 +481,21 @@
390
+ if (recv_rc>=0) message1[recv_rc]='\0';
394
+ for (message = strtok_r(message1, "\n", &token_ptr);
396
+ message=strtok_r(NULL, "\n", &token_ptr)){
398
if (message[0] != 27)
400
message[recv_rc] = '\0';
401
- debug ("Message: %s, length: %d", message, recv_rc);
402
+ debug ("Message: %s, length: %d", message, strlen(message));
403
if ((strlen (message) + strlen (morsetext)) <= MAXMORSE - 1)
404
strcat (morsetext, message);
406
+ debug(" Morsetext: %s, length: %d", morsetext, strlen(morsetext));
410
{ /* check ESCAPE characters */
416
+ kill_timer(last_id);
419
cwdev->reset (cwdev);
420
debug ("Reset all values");
422
@@ -704,66 +639,86 @@
423
if (bandswitch <= 15 && bandswitch >= 0)
424
set_switch (bandswitch);
431
+ case 'f': /* send echo to main program when CW playing is done */
432
+ memcpy(&reply_sin, &k_sin, sizeof(reply_sin)); /* remember sender */
433
+ reply_socklen = sin_len;
434
+ strncpy(reply_data, message, sizeof(reply_data) - 1);
435
+ reply_data[sizeof(reply_data) - 1] = '\0';
436
+ if (strlen (message) + 1 <= MAXMORSE - 1) strcat (morsetext, "^");
443
+ if (!morse_playing) { /* No chars in queue. Maybe active pttoff. We start playing */
447
+ kill_timer(last_id);
448
+ /* last_id=-1; NO! */
450
+ cwdev->ptt (cwdev, ON);
451
+ debug("PTT_delay on (%d)", ptt_delay);
452
+ if (last_id>=0) /* waiting for pttoff, we don't need pttdelay */
453
+ last_id=install_timer(0, playmorse, NULL); /* start immediatelly */
455
+ last_id=install_timer(ptt_delay, pttdelay, NULL); /* start after pttdelay */
457
+ last_id=install_timer(0, playmorse, NULL); /* start immediatelly */
466
/* check every character for speed increase or decrease, convert other
467
- characters to morse and play them */
469
-playmorsestring (char *x)
471
+ characters to morse and store it into morse_playing and morse_i */
474
- /* stop ptt timer */
477
- cwdev->ptt (cwdev, ON);
480
- udelay (ptt_delay);
484
- char c = islower (*x) ? toupper (*x) : *x;
485
- if ((c == '+') || (c == '-'))
486
- { /* speed in- & decrease */
487
- if ((c == '+') && (morse_speed <= 58))
489
- if ((c == '-') && (morse_speed >= 10))
493
- morse_gap = 15; /* half dash time additional for the next char */
496
- morse *m = chartomorse (c);
502
- if (i >= strlen (morsetext))
510
- morsetext[0] = '\0';
511
- /* start ptt off timer */
514
- gettimeofday (&end, NULL);
515
- timer_add (&end, 0, dash / morse_speed);
516
- ptt_timer_running = 1;
518
+void get_next_char(void){
521
+ for (x=morsetext; *x!='\0'; x++){
522
+ switch(toupper(*x)){
524
+ if (morse_speed<=58) morse_speed+=2;
528
+ if (morse_speed>=10) morse_speed-=2;
532
+ morse_gap = 15; /* half dash time additional for the next char */
536
+ if (strlen(reply_data)==0) return;
537
+ sendto(socket_descriptor, reply_data, strlen(reply_data), 0,
538
+ (struct sockaddr *)&reply_sin, reply_socklen);
539
+ strcpy(reply_data,"");
543
+ morse_playing = chartomorse (toupper(*x));
545
+ if (strlen(morsetext)>0) memmove(morsetext, morsetext+1, strlen(morsetext));
547
+ if (!morse_playing) debug("get_next_char: NULL");
548
+ else debug("get_next_char: '%c'", morse_playing->code);
554
+ morse_playing = NULL;
556
+ return; /* no morse found */
560
/* parse the command line and check for options, do some error checking */
562
parsecommandline (int argc, char *argv[])
563
@@ -874,13 +829,30 @@
567
+void terminate_all_subsystems(void){
570
+ cwdev->free (cwdev);
571
+ close_rc = close (socket_descriptor);
572
+ if (close_rc == -1)
574
+ errmsg ("Close socket");
579
+void initialize(void){
580
+ set_handlers(socket_descriptor, recv_code, NULL, NULL, NULL);
584
/* main program: fork, open network connection and go into an endless loop
585
waiting for something to happen on the UDP port */
587
main (int argc, char *argv[])
590
- int bind_rc, close_rc;
592
long save_file_flags;
594
#ifdef HAVE_LINUX_PPDEV_H
595
@@ -961,31 +933,10 @@
603
- udelay (1000); /*prevent 100% CPU */
605
- playmorsestring (morsetext);
606
- /* check for ptt off timer */
607
- if (1 == ptt_timer_running)
609
- gettimeofday (&now, NULL);
610
- if (timer_sub (&left, &end, &now) <= 0)
612
- cwdev->ptt (cwdev, OFF);
614
- ptt_timer_running = 0;
618
+ select_loop(initialize);
620
- cwdev->free (cwdev);
621
- close_rc = close (socket_descriptor);
622
- if (close_rc == -1)
624
- errmsg ("Close socket");
627
+ terminate_all_subsystems();
630
diff -ruN cwdaemon-0.7/cwdaemon.h cwdaemon-0.7-zia/cwdaemon.h
631
--- cwdaemon-0.7/cwdaemon.h 2003-10-26 20:18:03.000000000 +0100
632
+++ cwdaemon-0.7-zia/cwdaemon.h 2003-11-25 12:07:53.000000000 +0100
640
+#ifdef HAVE_SYS_TIME_H
641
+#include <sys/time.h>
644
+#include <sys/types.h>
650
+#ifdef HAVE_SYS_WAIT_H
651
+#include <sys/wait.h>
658
typedef struct cwdev_s
660
int (*init) (struct cwdev_s *);
662
void errmsg (char *info, ...);
663
void debug (char *info, ...);
665
+void get_next_char(void);
666
+void playmorse (void *xxx);
667
+void playpause (void *xxx);
668
+void pttdelay (void *xxx);
669
+void offdelay (void *xxx);
670
+void tune(int onoff);
671
+void tune_pttdelay (void *xxx);
672
+void tune_offdelay (void *xxx);
675
#ifdef HAVE_LINUX_PPDEV_H
676
int lp_init (cwdevice * dev);
677
int lp_free (cwdevice * dev);
679
int ttys_cw (cwdevice * dev, int onoff);
680
int ttys_ptt (cwdevice * dev, int onoff);
692
+ struct xlist_head *next;
693
+ struct xlist_head *prev;
698
+#define init_list(x) {(x).next=&(x); (x).prev=&(x);}
699
+#define list_empty(x) ((x).next == &(x))
700
+#define del_from_list(x) {((struct list_head *)(x)->next)->prev=(x)->prev; ((struct list_head *)(x)->prev)->next=(x)->next;}
701
+/*#define add_to_list(l,x) {(x)->next=(l).next; (x)->prev=(typeof(x))&(l); (l).next=(x); if ((l).prev==&(l)) (l).prev=(x);}*/
702
+#define add_at_pos(p,x) do {(x)->next=(p)->next; (x)->prev=(p); (p)->next=(x); (x)->next->prev=(x);} while(0)
705
+#define add_to_list(l,x) add_at_pos((typeof(x))&(l),(x))
706
+#define foreach(e,l) for ((e)=(l).next; (e)!=(typeof(e))&(l); (e)=(e)->next)
707
+#define foreachback(e,l) for ((e)=(l).prev; (e)!=(typeof(e))&(l); (e)=(e)->prev)
709
+#define add_to_list(l,x) add_at_pos((struct xlist_head *)(void*)&(l),(struct xlist_head *)(x))
710
+#define foreach(e,l) for ((e)=(l).next; (e)!=(void *)&(l); (e)=(e)->next)
711
+#define foreachback(e,l) for ((e)=(l).prev; (e)!=(void *)&(l); (e)=(e)->prev)
713
+#define free_list(l) {while ((l).next != &(l)) {struct list_head *a=(l).next; del_from_list(a); mem_free(a); }}
716
+typedef unsigned tcount;
718
+extern int terminate;
720
+long select_info(int);
721
+void select_loop(void (*)());
722
+int register_bottom_half(void (*)(void *), void *);
723
+void check_bottom_halves();
724
+int install_timer(ttime, void (*)(void *), void *);
725
+void kill_timer(int);
726
+ttime get_timer_time(int);
734
+void *get_handler(int, int);
736
+void set_handlers(int, void (*)(void *), void (*)(void *), void (*)(void *), void *);
737
+void install_signal_handler(int, void (*)(void *), void *, int);
742
+extern struct timeval start;
743
+#define ST_START gettimeofday(&start, NULL)
745
+ struct timeval stop;\
747
+ gettimeofday(&stop, NULL);\
748
+ usec=stop.tv_usec-start.tv_usec;\
749
+ sec=stop.tv_sec-start.tv_sec;\
750
+ if (usec<0){usec+=1000000;sec--;}\
751
+ dbg("stopky: %s: %d.%06d \n", __FUNCTION__, sec,usec);\
754
+#define ST_START gettimeofday(&start, NULL)
756
+ struct timeval stop;\
758
+ gettimeofday(&stop, NULL);\
759
+ usec=stop.tv_usec-start.tv_usec;\
760
+ sec=stop.tv_sec-start.tv_sec;\
761
+ if (usec<0){usec+=1000000;sec--;}\
762
+ dbg("stopky: %s: %d.%06d \n", __FUNCTION__, sec,usec);\
766
#endif /* _CWDAEMON_H */
767
--- cwdaemon-0.7/select.c 1970-01-01 01:00:00.000000000 +0100
768
+++ cwdaemon-0.7-zia/select.c 2003-11-25 12:15:59.000000000 +0100
770
+#include "cwdaemon.h"
773
+ This file is stolen from links
775
+struct timeval start;
778
+ void (*read_func)(void *);
779
+ void (*write_func)(void *);
780
+ void (*error_func)(void *);
784
+struct thread threads[FD_SETSIZE];
796
+static int timer_id = 1;
799
+ struct timer *next;
800
+ struct timer *prev;
802
+ void (*func)(void *);
807
+struct list_head timers = {&timers, &timers};
812
+ gettimeofday(&tv, NULL);
813
+ return tv.tv_sec * 1000 + tv.tv_usec / 1000;
816
+struct bottom_half {
817
+ struct bottom_half *next;
818
+ struct bottom_half *prev;
819
+ void (*fn)(void *);
823
+struct list_head bottom_halves = { &bottom_halves, &bottom_halves };
825
+int register_bottom_half(void (*fn)(void *), void *data)
827
+ struct bottom_half *bh;
828
+ foreach(bh, bottom_halves) if (bh->fn == fn && bh->data == data) return 0;
829
+ if (!(bh = malloc(sizeof(struct bottom_half)))) return -1;
832
+ add_to_list(bottom_halves, bh);
836
+void check_bottom_halves()
838
+ struct bottom_half *bh;
839
+ void (*fn)(void *);
842
+ if (list_empty(bottom_halves)) return;
843
+ bh = bottom_halves.prev;
852
+#define CHK_BH if (!list_empty(bottom_halves)) check_bottom_halves();
858
+ ttime interval = get_time() - last_time;
860
+ foreach(t, timers) t->interval -= interval;
862
+ foreach(t, timers) if (t->interval <= 0) {
863
+ struct timer *tt = t;
865
+ tt->func(tt->data);
870
+ last_time += interval;
873
+int install_timer(ttime t, void (*func)(void *), void *data)
875
+ struct timer *tm, *tt;
878
+ //debug("install_timer(%ld.%06ld, %p)", t/1000000, t%1000000, func);
879
+ if (!(tm = malloc(sizeof(struct timer)))) return -1;
883
+ tm->id = timer_id++;
884
+ foreach(tt, timers) if (tt->interval >= t) break;
885
+ add_at_pos(tt->prev, tm);
889
+ttime get_timer_time(int id)
892
+ foreach(tm, timers) if (tm->id == id) {
893
+ return tm->interval;
899
+void kill_timer(int id)
903
+ foreach(tm, timers) if (tm->id == id) {
904
+ struct timer *tt = tm;
913
+void set_handlers(int fd, void (*read_func)(void *), void (*write_func)(void *), void (*error_func)(void *), void *data)
915
+/* errmsg("set_handlers(%d,%p,%p,%p,%p)\n",fd,read_func,write_func,error_func,data);*/
917
+ if (fd < 0 || fd >= FD_SETSIZE) {
920
+ threads[fd].read_func = read_func;
921
+ threads[fd].write_func = write_func;
922
+ threads[fd].error_func = error_func;
923
+ threads[fd].data = data;
924
+ if (read_func) FD_SET(fd, &w_read);
926
+ FD_CLR(fd, &w_read);
927
+ FD_CLR(fd, &x_read);
929
+ if (write_func) FD_SET(fd, &w_write);
931
+ FD_CLR(fd, &w_write);
932
+ FD_CLR(fd, &x_write);
934
+ if (error_func) FD_SET(fd, &w_error);
936
+ FD_CLR(fd, &w_error);
937
+ FD_CLR(fd, &x_error);
939
+ if (read_func || write_func || error_func) {
940
+ if (fd >= w_max) w_max = fd + 1;
941
+ } else if (fd == w_max - 1) {
943
+ for (i = fd - 1; i >= 0; i--)
944
+ if (FD_ISSET(i, &w_read) || FD_ISSET(i, &w_write) ||
945
+ FD_ISSET(i, &w_error)) break;
953
+void select_loop(void (*init)())
959
+ last_time = get_time();
960
+ signal(SIGPIPE, SIG_IGN);
963
+ while (!terminate) {
966
+ struct timeval *tm = NULL;
968
+ if (!list_empty(timers)) {
969
+ ttime tt = ((struct timer *)(void*)&timers)->next->interval + 1;
970
+ if (tt < 0) tt = 0;
971
+ tv.tv_sec = tt / 1000;
972
+ tv.tv_usec = (tt % 1000) * 1000;
975
+ memcpy(&x_read, &w_read, sizeof(fd_set));
976
+ memcpy(&x_write, &w_write, sizeof(fd_set));
977
+ memcpy(&x_error, &w_error, sizeof(fd_set));
978
+ if (terminate) break;
979
+ if (!w_max && list_empty(timers)) break;
982
+/* if (tm) debug("select() tm=%ld.%06ld", tm->tv_sec, tm->tv_usec);*/
983
+ n = select(w_max, &x_read, &x_write, &x_error, tm);
985
+ if (errno != EINTR) {
986
+ errmsg("ERROR: select failed: %d", errno);
987
+ if (errno == EBADF){
993
+ for (i = 0; i < 256; i++) if (FD_ISSET(i, &x_read)) errmsg("%d,", i);
995
+ for (i = 0; i < 256; i++) if (FD_ISSET(i, &x_write)) errmsg("%d,", i);
997
+ for (i = 0; i < 256; i++) if (FD_ISSET(i, &x_error)) errmsg("%d,", i);
1000
+ for (i = 0; i < 256; i++) {
1005
+ if (select(i+1, &x, NULL, NULL, &tv)==0)
1018
+ while (n > 0 && ++i < w_max) {
1020
+ if (FD_ISSET(i, &x_read)) {
1021
+ if (threads[i].read_func) {
1022
+ threads[i].read_func(threads[i].data);
1027
+ if (FD_ISSET(i, &x_write)) {
1028
+ if (threads[i].write_func) {
1029
+ threads[i].write_func(threads[i].data);
1034
+ if (FD_ISSET(i, &x_error)) {
1035
+ if (threads[i].error_func) {
1036
+ threads[i].error_func(threads[i].data);