From: Maciej Sobczak on
On 19 Wrz, 19:02, "Dmitry A. Kazakov" <mail...(a)dmitry-kazakov.de>
wrote:

> Note that task termination is usually a difficult problem in Ada. You
> should pay an attention to this early.

No problem with that. With separate protected object (instead of
rendezvous) it is enough to do this:

protected type Channel_State is
procedure Post (J : in Job_Type);
procedure Finish;
entry Get_Job (J : out Job_Type; Finished : out Boolean);
function Busy return Boolean;
private
State : Worker_State := Idle;
Job_To_Do : Job_Type;
Should_Finish : Boolean := False;
end Channel_State;

and later in the worker task:

loop
Ch.all.Get_Job (Job, Finished);
exit when Finished;

-- do the job ...

end loop;


There is no need to introduce any special type of job ("poison pill").
Above, the job space is not polluted with task lifetime management
concepts - these should be kept separate.

--
Maciej Sobczak * www.msobczak.com * www.inspirel.com

Database Access Library for Ada: www.inspirel.com/soci-ada
From: Maciej Sobczak on
On 20 Wrz, 01:01, a...(a)anon.org (anon) wrote:

> Since you are using Channels, I am assuming that your talking about TCP/IP
> servers.

Not necessarily, although in this case it is indeed true.

> In this case you should look into using the Check_Selector

Why? If I want to keep the number of pipelines relatively small, then
fixed pool of tasks seems to be quite clean.

> Now the way GNAT has written the Selector function and the way the C_Select
> is written the maximum number of server per Check_Selector is 27.

Is it documented somewhere?
The only "documentation" I have found is the .ads file for
GNAT.Sockets, and this detail is not mentioned there.

Also, on my system the default limit on the number of file descriptors
used with select(2) is 1024.

> That is,
> each C_Select function can only monitor 32 sockets

What is C_Select? Why would it be more limited than select(2) (the
system-level one)?

--
Maciej Sobczak * www.msobczak.com * www.inspirel.com

Database Access Library for Ada: www.inspirel.com/soci-ada
From: Dmitry A. Kazakov on
On Sun, 21 Sep 2008 10:30:37 -0700 (PDT), Maciej Sobczak wrote:

> On 19 Wrz, 19:02, "Dmitry A. Kazakov" <mail...(a)dmitry-kazakov.de>
> wrote:
>
>> Note that task termination is usually a difficult problem in Ada. You
>> should pay an attention to this early.
>
> No problem with that. With separate protected object (instead of
> rendezvous) it is enough to do this:

Of course there is a problem because conditional and timed entry calls may
not contain a "terminate" alternative. This is another reason why a
protected object might be a poor choice. But the way you used protected
object looks like a design noise. Technically you just replaced a
rendezvous queue with a protected entry queue. Instead of just server and
workers, you have server, workers, channels and management stuff. Very
OO-ish in the negative sense of this word.

The second problem which adds complexity is server to worker 1-n
communication. Should be n-1 worker to server, simpler (classic
client-server) and more efficient too.

Due to complexity you have overlooked to make a channel idle again after a
job is done. You also have overlooked to add a "Shut_Down" state in order
to finalize workers: four channel states instead of just none. And note,
that the type Channel cannot call to the procedure Finish to set Shut_Down
from its Finalize (if it were controlled). That is because Worker is a
component of. The thing will hang up on channel destruction.

> There is no need to introduce any special type of job ("poison pill").

Job carries the parameters of a worker. You used it too, under the name
Job_Type.

> Above, the job space is not polluted with task lifetime management
> concepts - these should be kept separate.

You have polluted it with the procedure Finish, that has to be called
outside the worker tasks.

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
From: Maciej Sobczak on
On 21 Wrz, 21:24, "Dmitry A. Kazakov" <mail...(a)dmitry-kazakov.de>
wrote:
> On Sun, 21 Sep 2008 10:30:37 -0700 (PDT), Maciej Sobczak wrote:

> > No problem with that. With separate protected object (instead of
> > rendezvous) it is enough to do this:
>
> Of course there is a problem because conditional and timed entry calls may
> not contain a "terminate" alternative.

They don't have to. Again:

entry Get_Job (J : out Job_Type; Finished : out Boolean);

They have *no choice* but to get the Finished flag. Well, they don't
have to react to it, but that seems to be very easy to spot in the
code review or during the data flow analysis.

> But the way you used protected
> object looks like a design noise. Technically you just replaced a
> rendezvous queue with a protected entry queue.

The fact that this replacement is so obvious is exactly the reason why
rendezvous as a language-level mechanism is nothing but a syntax
sugar.

> Instead of just server and
> workers

There is never "just server and workers". There are also jobs, queues
(even if implicit) and lifetime management (your "poison pills").

> you have server, workers, channels and management stuff.

Because it *is* there anyway.
Trying to hide it? What about the following: in my version the main
task (the one that invents jobs) can give the job to the worker task
when it is still working on the previous one, so that the main task
need not be blocked and can go on inventing more jobs. In other words,
main task and worker tasks need not wait for each other. This is not
obvious from the state transitions that I've shown, but can be
trivially implemented this way, without any massive modification of
the tasks themselves:

protected type Channel_State is
procedure Post (J : in Job_Type);
procedure Finish;
entry Get_Job (J : out Job_Type; Finished : out Boolean);
function Can_Accept_New_Job return Boolean;
private
Job_Pending : Boolean := False;
Should_Finish : Boolean := False;
Job_To_Do : Job_Type;
end Channel_State;

This code makes it easy to go to real queue with capacity > 1.

This is not so easy in your version with rendezvous, where the main
task has to always *wait* until some of the worker will be willing to
pick up the new job (that's the whole point of rendezvous). Obviously,
there are fewer opportunities for concurrency in your version.
What would you have to do to have this increased concurrency in your
version? Would you still claim it to be simpler?

> The second problem which adds complexity is server to worker 1-n
> communication. Should be n-1 worker to server, simpler (classic
> client-server) and more efficient too.

I'd prefer producer + queue + N consumers. Still a protected object in
the middle.

> Due to complexity you have overlooked to make a channel idle again after a
> job is done.

Yes, I have overlooked it, but now I would not make it this way. I
would make it as describe above, to benefit from the increased
concurrency.

> And note,
> that the type Channel cannot call to the procedure Finish to set Shut_Down
> from its Finalize (if it were controlled).

That's right and this is why I asked here about different design
options.

> > There is no need to introduce any special type of job ("poison pill").
>
> Job carries the parameters of a worker. You used it too, under the name
> Job_Type.

But in my case the Job_Type need not contain the special "poison"
value. Let's say that the Job_Type is a matrix that has to be
reversed. There is no place for the "poison" value in the matrix
itself, so the Job_Type would need to be extended (a record with
additional flag, perhaps?) to cover it, but then the pollution of the
type would be very explicit.

> > Above, the job space is not polluted with task lifetime management
> > concepts - these should be kept separate.
>
> You have polluted it with the procedure Finish, that has to be called
> outside the worker tasks.

I did not pollute the Job_Type (the matrix to be reversed), only the
Channel_State - which is there exactly for everything that is related
to managing the pipeline.

--
Maciej Sobczak * www.msobczak.com * www.inspirel.com

Database Access Library for Ada: www.inspirel.com/soci-ada
From: anon on

Why use "GNAT.Sockets.Check_Selection"?

Well, in TCP/IP system the designers of TCP/IP created a routine called
TCP/IP "Select", that is designed to allow the use of multi-servers or
multitasking type of servers within one environment. There are reasons
that goes beyond Ada and other languages that states why a multi server
design should use the TCP/IP "Select" routine instead of multi tasks where
each task uses a TCP/IP "Accept" statements to block the server routine
until a call or a connection is made, basically it has to do with limited
system resources.

Note: TCP/IP "Accept" can only monitor one connection port while the
TCP/IP "Select" allows monitoring up to 32 at one time in one routine.
which makes the TCP/IP "Select" a better utilization of system resources.

Now, in GNAT the TCP/IP "Select" routine is ported to Ada by the function
"GNAT.Sockets.Thin.C_Select" which is the low-level C and non-portable
version of the routine. The non-portable TCP/IP "Select" is wrapped into
a procedure called "GNAT.Sockets.Check_Selection" that is more portable.

Why only 27 servers per TCP/IP "Select" routine Well, the designers created
the TCP/IP "Select" to use 3 32-bit word to detect or flag, up to 32 servers
(bitmapped). Now, the TCP/IP system uses the first 3 file descriptors mapped
onto the first 3 sockets positions and are defined as the standard system I/O
(Stdin (0), Stdout (1), Stderr (2)) which is hard coded in the TCP/IP "Select"
routine. And the designer of the "GNAT.Sockets" uses two additional random
assigned sockets or file descriptors (depends on port/version of GNAT) to build
the Selector type for interfacing with the TCP/IP "Select" routine. So,

27 (User Allowed sockets) :=
32 (total sockets) - 3 (System defined) - 2 (GNAT RTL used)


Note: For Full documentation on why 32 sockets you will need to check
with the history of TCP/IP for the full story, but I would guess it
was because of limited resources back then. Most servers back then only
handle a few services, and that has not changed too much in todays world.
Today, only, the volume of clients has increased.

Plus, some system may have 1024 file descriptors, but the OS normally
limit the number of assigned/open file descriptors to 32 or less. And
that includes the 3 Standard I/O files.

-- TCP/IP Server Designed (GNAT)

Now, the best and "Tried and True" design for a multiple service server
is for a single batch designed monitor task with multi-service server task.
The monitor task setup and monitor 2 to 27 sockets. Then activate the
service server task, afterwards quickly reset and monitors/idles until
the next request is made. Simple and straight foreword design.

Why Best design: Uses less resources including CPU cycles. Which allow
the server tasks and other programs a timely access to their resource needs.

--
-- A simple outline for a multiple services type of TCP/IP server task
--
Server_Controller_Task:

Server_Number : Natural ; -- Set to maximum number of server task
-- value ranges 5..32 (27) ;

-- Initialize Controller variables

for Server_task_count in 5..Server_Number loop
idle until called ( Ada "Accept" statement ) ( socket )
map socket to the Signalling_Fds (read/write/exception)
end loop


-- Monitoring Routine: idle until a server is needed.

loop
setup or reset Selector variables -- required for every call
call Check_Selector function -- Idle until connected
Using returned Signalling_Fds, find and call the server task to handle job
end loop

exception
shutdown server in case of TCP/IP system exception. Or
Storage_Error, Program_Error, etc.

--
-- Each "Server_Service_Task" handles one type of service.
--
Server_Service_Task: 5..MAX(32):

-- Initialize: Inform controller task that server exist
-- and is ready to handle request.

Create Socket
Bind Socket to port and address

-- This call assign and map Socket to Signalling_Fds value
-- which allows the controller server to monitor the
-- connection request and active this server when needed.
Call controller_task ( Socket )


-- idle task until called. Then handle request.

loop
Set server resources to idle
idle until called to handle server job. ( Ada "Accept" statement )
Open Channel
Handle server service job
Close Channel
exception
handle lost of connection. Reset for next call
end loop
exception
handle Socket_Error in case of TCP/IP Socket exception.

shutdown server in case of TCP/IP system exception. Or
Storage_Error, Program_Error, etc.




In <2cfc647a-c9cb-4e0c-9909-7923575fd1ec(a)d1g2000hsg.googlegroups.com>, Maciej Sobczak <see.my.homepage(a)gmail.com> writes:
>On 20 Wrz, 01:01, a...(a)anon.org (anon) wrote:
>
>> Since you are using Channels, I am assuming that your talking about TCP/I=
>P
>> servers.
>
>Not necessarily, although in this case it is indeed true.
>
>> In this case you should look into using the Check_Selector
>
>Why? If I want to keep the number of pipelines relatively small, then
>fixed pool of tasks seems to be quite clean.
>
>> Now the way GNAT has written the Selector function and the way the C_Sele=
>ct
>> is written the maximum number of server per Check_Selector is 27.
>
>Is it documented somewhere?
>The only "documentation" I have found is the .ads file for
>GNAT.Sockets, and this detail is not mentioned there.
>
>Also, on my system the default limit on the number of file descriptors
>used with select(2) is 1024.
>
>>=A0That is,
>> each C_Select function can only monitor 32 sockets
>
>What is C_Select? Why would it be more limited than select(2) (the
>system-level one)?
>
>--
>Maciej Sobczak * www.msobczak.com * www.inspirel.com
>
>Database Access Library for Ada: www.inspirel.com/soci-ada

First  |  Prev  |  Next  |  Last
Pages: 1 2 3 4
Prev: Using GtkAda in Ubuntu
Next: gnat on opensuse 11.1