From: Warren on
I some types defined as:

type Thread_Context is private;
type Stack_Area is new String;
type Thread_Proc is access procedure;

and an Ada API function:

procedure Thread_Start(Context : out Thread_Context; Proc :
Thread_Proc; Stack : out Stack_Area) is
type C_Func_Ptr is access procedure;
pragma Convention(Convention => C, Entity => C_Func_Ptr);

procedure avr_thread_start(Context : System.Address;
Proc : C_Func_Ptr;
Stack : System.Address;
Size : Unsigned_16);
pragma import(C,avr_thread_start,"avr_thread_start");

Func : C_Func_Ptr := Proc.all'Access;
begin


avr_thread_start(Context'Address,Func,Stack(Stack'First)'Address,Stack'Length);

end Thread_Start;

Because this part of the Ada code is not working, I'm suspicious that
the passed function pointer is incorrect (I have an all-C thread
example
working).

Is it vital that the Thread_Proc procedure also be declared as

pragma import(C,Proc) ?

I'd prefer not to, if possible.

There are no arguments passed-- I only need to launch the passed
Ada procedure from the C side (in a thread).

The avr_thread_start C routine is declared as:

void avr_thread_start(
avr_thread_context* context,
void (*fn)(void),
uint8_t* stack,
uint16_t stack_size
);

Warren
From: Dmitry A. Kazakov on
On Wed, 4 Aug 2010 09:40:29 -0700 (PDT), Warren wrote:

> I some types defined as:
>
> type Thread_Context is private;

pragma Convention (C, Thread_Context);

> type Stack_Area is new String;

pragma Convention (C, Stack_Area); -- Won't work with String

> type Thread_Proc is access procedure;

pragma Convention (C, Thread_Proc); -- Called from C

> and an Ada API function:
>
> procedure Thread_Start(Context : out Thread_Context;

Context : access Thread_Context;
-- Prevent temp object, if the type is not limited

> Proc : Thread_Proc;
> Stack : out Stack_Area) is

Stack : access Stack_Area

> procedure avr_thread_start(Context : System.Address;
> Proc : C_Func_Ptr;

Proc : Thread_Proc;

> Stack : System.Address;
> Size : Unsigned_16);
> pragma import(C,avr_thread_start,"avr_thread_start");
>
> Func : C_Func_Ptr := Proc.all'Access;
> begin
>
> avr_thread_start(Context'Address,Func,Stack(Stack'First)'Address,Stack'Length);
>
> end Thread_Start;
>
> Because this part of the Ada code is not working, I'm suspicious that
> the passed function pointer is incorrect (I have an all-C thread
> example working).

You have to pass a pointer to a procedure of C convention. You also must
ensure that other parameters aren't passed by copy.

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
From: Jeffrey Carter on
On 08/04/2010 09:40 AM, Warren wrote:
> I some types defined as:
>
> type Thread_Context is private;
> type Stack_Area is new String;
> type Thread_Proc is access procedure;
>
> and an Ada API function:
>
> procedure Thread_Start(Context : out Thread_Context; Proc :
> Thread_Proc; Stack : out Stack_Area) is
> type C_Func_Ptr is access procedure;
> pragma Convention(Convention => C, Entity => C_Func_Ptr);
>
> procedure avr_thread_start(Context : System.Address;
> Proc : C_Func_Ptr;
> Stack : System.Address;
> Size : Unsigned_16);
> pragma import(C,avr_thread_start,"avr_thread_start");
>
> Func : C_Func_Ptr := Proc.all'Access;
> begin
>
>
> avr_thread_start(Context'Address,Func,Stack(Stack'First)'Address,Stack'Length);
>
> end Thread_Start;
>
> Because this part of the Ada code is not working, I'm suspicious that
> the passed function pointer is incorrect (I have an all-C thread
> example
> working).
>
> Is it vital that the Thread_Proc procedure also be declared as
>
> pragma import(C,Proc) ?
>
> I'd prefer not to, if possible.
>
> There are no arguments passed-- I only need to launch the passed
> Ada procedure from the C side (in a thread).
>
> The avr_thread_start C routine is declared as:
>
> void avr_thread_start(
> avr_thread_context* context,
> void (*fn)(void),
> uint8_t* stack,
> uint16_t stack_size
> );

I'm presuming that you want Ada to call Thread_Start and not need to take
special steps because Thread_Start calls some C. In that case, you should ignore
most of what Kazakov said: The Ada-side declarations should not reflect the
implementation.

The 1st step is to translate the C side into something meaningful. "uint8_t*"
can mean a host of things:

* in uint8_t (uncommon)
* in out uint8_t
* out uint8_t
* in array_of_uint8_t
* in out array_of_uint8_t
* out array_of_uint8_t

I'll presume from your use of "out" that "context" and "stack" are "out", and
from your use of "new String" that "stack" is an array.

Next, you need to use convention-C types for your C parameters. There is no
guarantee that System.Address corresponds to a C pointer; if you're at all
interested in compiler portability, you won't use it. You also won't use "new
String" for the "uint8_t*" parameter.

So I'd do something like (untested):

procedure Thread_Start(Context : out Thread_Context;
Proc : in Thread_Proc;
Stack : out Stack_Area)
is
type C_Context is ...;
-- Something C-compatible that corresponds to the C
-- definition of avr_thread_context, using only
-- convention-C components.
pragma Convention (C, C_Context);

procedure Local_Proc is
-- null;
begin -- Local_Proc
Proc.all;
end Local_Proc;
pragma Convention (C, Local_Proc);

subtype Stack_String is Stack_Area (Stack'range);

type U8 is mod 2 ** 8;
pragma Convention (C, U8);

type U8_List is array (Stack'range) of U8;
pragma Convention (C, U8_List);

type U16 is mod 2 ** 16;
pragma Convention (C, U16);

procedure AVR_Thread_Start (Context : out C_Context;
Proc : access procedure;
Stack : out U8_List;
Size : in U16);
pragma Import (C, AVR_Thread_Start, "avr_thread_start");

Local_Context : C_Context;
Local_Stack : U8_List;

-- function To_Thread_Context (From : C_Context) return Thread_Context;
--
-- function To_Stack_Area (From : U8_List) return Stack_String;
-- Implementations left as exercises for the reader.
begin -- Thread_Start
AVR_Thread_Start (Context => Local_Context,
Proc => Local_Proc'access,
Stack => Local_Stack,
Size => Local_Stack'Length);
Context := To_Thread_Context (Local_Context);
Stack := To_Stack_Area (Local_Stack);
end Thread_Start;

Ada will pass Stack as a "U8*" pointing to the first component and Context as an
appropriate pointer; see ARM B.3.

Local_Context and Local_Stack give us convention-C objects to pass; Local_Proc a
convention-C procedure that C may call.

I presume Thread_Context is not opaque and its contents are accessed on the Ada
side. Otherwise you may be able to get away with a convention-C pointer to
Thread_Context.

Are Context and Stack really needed outside Thread_Start?

--
Jeff Carter
"Ah, go away or I'll kill ya."
Never Give a Sucker an Even Break
100

--- news://freenews.netfront.net/ - complaints: news(a)netfront.net ---
From: Dmitry A. Kazakov on
On Wed, 04 Aug 2010 11:46:56 -0700, Jeffrey Carter wrote:

> procedure Thread_Proc : out Thread_Context;
> Proc : in Thread_Proc;
> Stack : out Stack_Area)
> is
> type C_Context is ...;
> -- Something C-compatible that corresponds to the C
> -- definition of avr_thread_context, using only
> -- convention-C components.
> pragma Convention (C, C_Context);
>
> procedure Local_Proc is
> -- null;
> begin -- Local_Proc
> Proc.all;
> end Local_Proc;
> pragma Convention (C, Local_Proc);

I am not sure if this works everywhere. The problem is that Local_Proc
refers to a local variable Proc (formal parameter of Thread_Start). This
needs some special techniques on the compiler side to become C-conform,
e.g. a trampoline.

Local_Proc should be moved out of Thread_Proc.

Then a copy of Proc is created in the scope of. That would be not task safe
without a protected object around the copy of Proc.

Another way could be passing a copy of Proc on Thread_Context.

But IMO there is nothing with putting C conventions on Ada callbacks. All
bindings I know, use it without hesitation.

> Are Context and Stack really needed outside Thread_Start?

In any case they need to be collected upon thread completion. Therefore
they must come from outside (unless Thread_Proc could free them, which is
unlikely).

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
From: Simon Wright on
I would be a bit worried about Thread_Proc's *Ada* environment. There's
this thing called the Secondary Stack, used for - for example - string
catenation; and it's not set up here.

I think you'll be OK so long as you restrain yourself! Twiddling LEDs
should be OK.

There may be a pragma to prevent secondary stack usage?