fix memstream
[brdnet.git] / ServerLoop.pas
blobf73f58b91c27976ec9c4d6fd7d2cf6ae249f862b
1 UNIT ServerLoop;
3 INTERFACE
4 uses MemStream,NetAddr;
6 procedure Main;
8 type tSMsg=object
9 Source: ^tNetAddr;
10 Length: {Long}Word;
11 Data: pointer;
12 stream: tMemoryStream;
13 channel: word;
14 end;
16 type tMessageHandler=procedure(msg:tSMsg);
18 procedure SetMsgHandler(OpCode:byte; handler:tMessageHandler);
19 procedure SetHiMsgHandler(handler:tMessageHandler);
21 type tFDEventHandler=procedure(ev:Word) of object;
22 type tOnTimer=procedure of object;
24 procedure WatchFD(fd:tHandle; h:tFDEventHandler);
25 procedure Shedule(timeout{ms}: LongWord; h:tOnTimer);
26 procedure UnShedule(h:tOnTimer);
28 IMPLEMENTATION
30 USES SysUtils,Sockets,UnixType,BaseUnix
31 ,Unix
34 {aim for most simple implementation, since could be extended anytime}
36 var s_inet:tSocket;
38 type tPollTop=0..7;
39 var pollArr: packed array [tPollTop] of tPollFd;
40 type tFdHndDsc=record
41 cb: tFDEventHandler; {proc+object}
42 end;
43 var pollHnd: array [tPollTop] of tFdHndDsc;
44 var pollTop: tPollTop;
46 var hnd: array [1..36] of tMessageHandler;
47 var HiHnd: tMessageHandler;
49 type tSheduled_ptr=^tSheduled; tSheduled=record
50 left:LongWord;
51 cb:tOnTimer;
52 next:tSheduled_ptr;
53 end;
54 var ShedTop: ^tSheduled;
55 var ShedUU: ^tSheduled;
56 var LastShed: UnixType.timeval;
57 var PollTimeout:LongInt;
59 procedure IdleStuff;
60 begin write('.'); end;
62 procedure SC(fn:pointer; retval:cint);
63 begin
64 if retval < 0 then begin
65 raise eXception.Create(Format('Socket error %d operation %P',[SocketError,fn]));
66 end;
67 end;
69 procedure s_SetupInet;
70 var bind_addr:tInetSockAddr;
71 begin
72 with bind_addr do begin
73 family:=AF_INET;
74 port:=htons(3511);
75 addr:=0; {any}
76 s_inet:=fpSocket(family,SOCK_DGRAM,IPPROTO_UDP);
77 SC(@fpSocket,s_inet);
78 end;
79 SC(@fpBind,fpBind(s_inet,@bind_addr,sizeof(bind_addr)));
80 with PollArr[0] do begin
81 fd:=s_inet;
82 events:=pollIN;
83 revents:=0;
84 end;
85 end;
87 var Terminated:boolean=false;
89 procedure SendMessage(const data; len:word; const rcpt:tSockAddr );
90 var rc:Integer;
91 begin
92 SC(@fpsendto,fpsendto(s_inet,@data,len,0,@rcpt,sizeof(sockaddr_in)));
93 end;
95 procedure SignalHandler(sig:cint);CDecl;
96 begin
97 writeln;
98 if terminated then raise eControlC.Create('CtrlC DoubleTap') ;
99 Terminated:=true;
100 writeln('Shutdown requested');
101 end;
103 {do not waste stack on statics}
104 var EventsCount:integer;
105 var Buffer:array [1..1024] of byte;
106 var pkLen:LongWord;
107 var From:tSockAddrL; {use larger struct so everything fits}
108 var FromLen:LongWord;
109 var FromG:tNetAddr;
110 var curhnd:tMessageHandler;
111 var Msg:tSMsg;
112 var tp:tPollTop;
114 procedure PrepareHandler;
115 begin
116 FromG.FromSocket(from);
117 if Buffer[1]>128 then curhnd:=HiHnd else curhnd:=hnd[Buffer[1]];
118 if not assigned(curhnd) then raise eXception.Create('No handler for opcode '+IntToStr(Buffer[1]));
119 Msg.Source:=@FromG; {!thread}
120 Msg.Length:=pkLen;
121 Msg.Data:=@Buffer; {!thread}
122 Msg.stream.Init(@Buffer,pkLen,sizeof(Buffer));
123 Msg.channel:=0; {!multisocket}
124 end;
126 procedure ShedRun;
127 var cur:^tSheduled;
128 var pcur:^pointer;
129 var now:UnixType.timeval;
130 var delta:LongWord;
131 var olTop:^tSheduled;
132 begin
133 {Sheduling}
134 olTop:=ShedTop;
135 pcur:=@olTop;
136 cur:=pcur^;
137 ShedTop:=nil; {unlink the current shed list}
138 fpgettimeofday(@Now,nil);
139 delta:=(Now.tv_sec-LastShed.tv_sec);
140 if delta>6 then delta:=5000 else delta:=(delta*1000)+((Now.tv_usec-LastShed.tv_usec) div 1000);
141 LastShed:=Now;
142 writeln('DeltaTime: ',delta);
143 while assigned(cur) do begin
144 if cur^.left<delta then begin
145 cur^.cb;
146 pcur^:=cur^.next;
147 cur^.next:=ShedUU;
148 ShedUU:=cur;
149 cur:=pcur^;
150 end else begin
151 DEC(cur^.left,delta);
152 if pollTimeOut>cur^.left then PollTimeOut:=cur^.left;
153 //if pollTimeout>20 then pollTimeOut:=pollTimeOut div 2;
154 if pollTimeout=0 then pollTimeOut:=1;
155 pcur:=@cur^.next;
156 cur:=cur^.next;
157 end;
158 end;
159 pcur^:=ShedTop; {append newly added tasks to end of untriggererd list}
160 ShedTop:=olTop; {link in the untriggered tasks}
161 end;
163 procedure Main;
164 begin
165 s_setupInet;
166 while not terminated do begin
167 PollTimeout:=5000;
168 ShedRun;
169 EventsCount:=fpPoll(@PollArr[0],PollTop,PollTimeout);
170 if (eventscount=-1)and terminated then break;
171 if eventscount=-1 then break; {fixme: print error}
172 if eventscount=0 then IdleStuff else begin
173 {INET socket}
174 with PollArr[0] do begin
175 if (revents and pollIN)>0 then begin
176 FromLen:=sizeof(From);
177 pkLen:=fprecvfrom(s_inet,@Buffer,sizeof(Buffer),0,@from,@fromlen);
178 SC(@fprecvfrom,pkLen);
179 PrepareHandler;
180 curhnd(Msg);
181 revents:=0;
182 end;
183 end;
184 {INET6...}
185 {Generic}
186 for tp:=1 to pollTop do if PollArr[tp].revents>0 then begin
187 PollHnd[tp].CB(PollArr[tp].rEvents);
188 PollArr[tp].revents:=0;
189 end;
190 end;
191 end;
192 write('Loop broken [');
193 CloseSocket(s_inet);
194 writeln(']');
195 end;
197 procedure SetMsgHandler(OpCode:byte; handler:tMessageHandler);
198 begin hnd[OpCode]:=handler; end;
199 procedure SetHiMsgHandler(handler:tMessageHandler);
200 begin Hihnd:=handler; end;
202 procedure WatchFD(fd:tHandle; h:tFDEventHandler);
203 var opt: tPollTop;
204 begin
205 if assigned(h) then begin
206 PollHnd[pollTop].CB:=h;
207 PollArr[pollTop].fd:=fd;
208 PollArr[pollTop].events:=POLLERR or POLLHUP or POLLIN or POLLPRI or
209 POLLRDBAND or POLLRDNORM;
210 PollArr[pollTop].revents:=0;
211 //writeln('Add watch ',pollTop,' on ',fd,' to ',IntToHex(qword(@h),8));
212 Inc(PollTop);
213 end else for opt:=0 to high(opt) do if PollArr[opt].fd=fd then begin
214 if (pollTop-1)>opt then begin
215 PollArr[opt]:=PollArr[pollTop-1];
216 PollHnd[opt]:=PollHnd[pollTop-1];
217 end;
218 dec(pollTop);
219 PollArr[pollTop].fd:=-1;
220 PollArr[pollTop].events:=0;
221 PollArr[pollTop].revents:=0;
222 break;
223 end;
224 end;
226 procedure Shedule(timeout{ms}: LongWord; h:tOnTimer);
227 var old:^tSheduled;
228 begin
229 old:=ShedTop;
230 if Assigned(ShedUU) then begin
231 ShedTop:=ShedUU;
232 ShedUU:=ShedUU^.next;
233 end else New(ShedTop);
234 ShedTop^.Left:=timeout;
235 ShedTop^.CB:=h;
236 ShedTop^.Next:=old;
237 end;
239 procedure UnShedule(h:tOnTimer);
240 var cur:^tSheduled;
241 var pcur:^pointer;
242 begin
243 pcur:=@ShedTop;
244 cur:=pcur^;
245 while assigned(cur) do begin
246 if cur^.cb=h then begin
247 pcur^:=cur^.next; {unlink from main list}
248 cur^.next:=ShedUU; ShedUU:=cur; {link to unused}
249 break;
250 end else begin
251 pcur:=@cur^.next;
252 cur:=pcur^;
253 end;
254 end;
255 end;
257 var i:byte;
258 BEGIN
259 fpSignal(SigInt,@SignalHandler);
260 fpSignal(SigTerm,@SignalHandler);
261 for i:=1 to high(hnd) do hnd[i]:=nil;
262 pollTop:=1; {1 for basic listen}
263 ShedTop:=nil;
264 ShedUU:=nil;
265 fpgettimeofday(@LastShed,nil);
266 END.