diff mbox

[newlib] Allow locking routine to be retargeted

Message ID ce22a6fb-67bb-c1b2-78ea-669bd840ad67@foss.arm.com
State Superseded
Headers show

Commit Message

Thomas Preudhomme Nov. 10, 2016, 2:42 p.m. UTC
Hi,

At the moment when targeting bare-metal targets or systems without definition 
for the locking primitives, newlib uses dummy empty macros. This has the 
advantage of reduced size and faster implementation but does not allow the 
application to retarget the locking routines. Retargeting is useful for a single 
toolchain to support multiple systems since then it's only at build time that 
you know which system you are targeting.

This patch adds a new configure option --enable-newlib-retargetable-locking to 
use dummy empty weakly defined functions instead of dummy empty macros. The 
default is to keep the current behavior to not have any size or speed impact on 
targets not interested in this feature.

Testing: I've built a simple test program that calls malloc and free with all 
the locking function defined in the same file. Running into a debugger with 
breakpoints on the function shows that they are indeed called.

Is this ok for master branch?

Best regards,

Thomas

Comments

Freddie Chopin Nov. 10, 2016, 3:33 p.m. UTC | #1
Hi Thomas!

Why don't you do the same thing for recursive functions? At least the
lock used by malloc() has to be recursive, so with your patch exactly
all mutexes should be recursive too.

I have doubts about practical implementation of these functions for any
RTOS, because of the __LOCK_INIT() macro used for initialization. In
every retargeted function you'll have to start critical section (most
likely by disabling interrupts) to initialize the object on heap, but
then how would you use heap if malloc()'s lock is used via these
functions too?

Regards,
FCh
Thomas Preudhomme Nov. 10, 2016, 4:04 p.m. UTC | #2
Hi Freddie,

Thanks for your feedback.

On 10/11/16 15:33, Freddie Chopin wrote:
> Hi Thomas!

>

> Why don't you do the same thing for recursive functions? At least the

> lock used by malloc() has to be recursive, so with your patch exactly

> all mutexes should be recursive too.


Yes, I did not want to expose all functions until there is a need to 
differentiate. it is easy to add them later but removing it if noone use it 
would be quite difficult.

>

> I have doubts about practical implementation of these functions for any

> RTOS, because of the __LOCK_INIT() macro used for initialization. In

> every retargeted function you'll have to start critical section (most

> likely by disabling interrupts) to initialize the object on heap, but

> then how would you use heap if malloc()'s lock is used via these

> functions too?


I do not understand. Why would __LOCK_INIT need to start a critical section?

Best regards,

Thomas
Freddie Chopin Nov. 10, 2016, 8:32 p.m. UTC | #3
Hello again!

On Thu, 2016-11-10 at 16:04 +0000, Thomas Preudhomme wrote:
> > Why don't you do the same thing for recursive functions? At least

> > the

> > lock used by malloc() has to be recursive, so with your patch

> > exactly

> > all mutexes should be recursive too.

> 

> Yes, I did not want to expose all functions until there is a need to 

> differentiate. it is easy to add them later but removing it if noone

> use it 

> would be quite difficult.


But there is a need to differentiate! Recursive lock is used 10 times
in newlib (for example in malloc() / free()), non-recursive is used 4
times (for example for locking time-zone info). If you only allow
overriding the "standard" functions, you effectively force the user to
use recursive locks everywhere. I'd definitely be "for" providing
functions for both lock types!

> > I have doubts about practical implementation of these functions for

> > any

> > RTOS, because of the __LOCK_INIT() macro used for initialization.

> > In

> > every retargeted function you'll have to start critical section

> > (most

> > likely by disabling interrupts) to initialize the object on heap,

> > but

> > then how would you use heap if malloc()'s lock is used via these

> > functions too?

> 

> I do not understand. Why would __LOCK_INIT need to start a critical

> section?


OK, this was not very clear. The problem with simple functions like the
ones in your patch is that they are extremely hard to actually use in
an RTOS. Or maybe it just seems so hard for me, that's also possible.

This problem affects only "static" locks created with __LOCK_INIT and
__LOCK_INIT_RECURSIVE macros. With your patch such locks are just
uintptr_t with value 0. From my understanding it seems that such locks
are considered to be "initialized". So you can call __lock_acquire() on
such object right away. But how would such function look like?

void __lock_acquire(_LOCK_T lock)
{
	if (lock == 0)
	{
		// lock not yet really initialized...
		disableInterrupts();
		if (lock == 0)	// re-check due to possible race condition
			lock = malloc(sizeof(RealMutexFromYourRtos));

		assert(lock != 0);
		rtos_mutex_create(lock);
		enableInterrupts();
	}

	rtos_mutex_lock((RealMutexFromYourRtos*)lock);
}

This could work, if only malloc() was not protected with the statically
created _LOCK_T lock itself, which would need such initialization
too... I don't see any other way to write such function for an RTOS
which has mutexes with size greater than "uintptr_t" (probably huge
majority). You could do it with a static pool of storage for mutexes,
but then you'd have to know upfront how many you want and your RTOS
would have to allow creation of locks in provided storage (most of them
allow that, but not all). Maybe I just don't see the simplest solution
to this problem?

Don't get me wrong - I'd really like something like this to be in
newlib. It just seems that this is extremely hard due to the
__LOCK_INIT macro which hardcodes the actual size of storage... I'm
really interested in that feature, as I'd like to provide better newlib
integration for the RTOS I'm writing ( http://distortos.org/ ).

Regasrds,
FCh
Freddie Chopin Nov. 10, 2016, 8:34 p.m. UTC | #4
On Thu, 2016-11-10 at 21:32 +0100, Freddie Chopin wrote:
> void __lock_acquire(_LOCK_T lock)

> {

> 	if (lock == 0)

> 	{

> 		// lock not yet really initialized...

> 		disableInterrupts();

> 		if (lock == 0)	// re-check due to possible race

> condition

> 			lock = malloc(sizeof(RealMutexFromYourRtos));

> 

> 		assert(lock != 0);

> 		rtos_mutex_create(lock);

> 		enableInterrupts();

> 	}

> 

> 	rtos_mutex_lock((RealMutexFromYourRtos*)lock);

> }


I've obviously missed some curly braces for the inner "if", but I guess
you get my idea anyway (;

Regards,
FCh
onkel.jack@t-online.de Nov. 11, 2016, 9:37 a.m. UTC | #5
Hello,

to unconditionally provide weak dummy implementation I dont think its a good idea since it has some implications:
If the newlib is compiled for single thread, no locks are required at all, so the dummy implementation/macro is doing the job to
satisfy the linker.
But for multithread its dangerous to provide a dummy since you would not see first hand there are no locks but dummys.

So my suggestion would be:
- For single thread leave the implementation as is.
- For multithread, dont provide any lock function, so linking newlib without to provide locking functions from os/kernel 
would result in a linkage error, therefore forces the user not to forget to provide them.

So I think, just put something like #ifndef MULTI_THREAD around the existing macros would do it. 






-----Original-Nachricht-----
Betreff: Re: [PATCH, newlib] Allow locking routine to be retargeted
Datum: 2016-11-10T21:35:30+0100
Von: "Freddie Chopin" <freddie_chopin@op.pl>
An: "newlib@sourceware.org" <newlib@sourceware.org>

On Thu, 2016-11-10 at 21:32 +0100, Freddie Chopin wrote:
> void __lock_acquire(_LOCK_T lock)

> {

> 	if (lock == 0)

> 	{

> 		// lock not yet really initialized...

> 		disableInterrupts();

> 		if (lock == 0)	// re-check due to possible race

> condition

> 			lock = malloc(sizeof(RealMutexFromYourRtos));

> 

> 		assert(lock != 0);

> 		rtos_mutex_create(lock);

> 		enableInterrupts();

> 	}

> 

> 	rtos_mutex_lock((RealMutexFromYourRtos*)lock);

> }


I've obviously missed some curly braces for the inner "if", but I guess
you get my idea anyway (;

Regards,
FCh

Thomas Preudhomme Nov. 11, 2016, 11:01 a.m. UTC | #6
On 11/11/16 09:37, onkel.jack@t-online.de wrote:
> Hello,

>

> to unconditionally provide weak dummy implementation I dont think its a good idea since it has some implications:

> If the newlib is compiled for single thread, no locks are required at all, so the dummy implementation/macro is doing the job to

> satisfy the linker.

> But for multithread its dangerous to provide a dummy since you would not see first hand there are no locks but dummys.


I don't see how is that different from what is happening now. Right now for 
bare-metal targets the locking macro are always void ones.

>

> So my suggestion would be:

> - For single thread leave the implementation as is.

> - For multithread, dont provide any lock function, so linking newlib without to provide locking functions from os/kernel

> would result in a linkage error, therefore forces the user not to forget to provide them.

>

> So I think, just put something like #ifndef MULTI_THREAD around the existing macros would do it.


That would defeat the purpose IMO. The point is to provide a single newlib build 
for both single threaded and multithreaded applications: newlib would be built 
for multithread environment and would work fine for single thread without need 
to define any function and multithread applications (or more likely the RTOS) 
can define locking functions to make it work.

Best regards,

Thomas
Thomas Preudhomme Nov. 11, 2016, 11:09 a.m. UTC | #7
On 10/11/16 20:32, Freddie Chopin wrote:
> Hello again!

>

> On Thu, 2016-11-10 at 16:04 +0000, Thomas Preudhomme wrote:

>>> Why don't you do the same thing for recursive functions? At least

>>> the

>>> lock used by malloc() has to be recursive, so with your patch

>>> exactly

>>> all mutexes should be recursive too.

>>

>> Yes, I did not want to expose all functions until there is a need to

>> differentiate. it is easy to add them later but removing it if noone

>> use it

>> would be quite difficult.

>

> But there is a need to differentiate! Recursive lock is used 10 times

> in newlib (for example in malloc() / free()), non-recursive is used 4

> times (for example for locking time-zone info). If you only allow

> overriding the "standard" functions, you effectively force the user to

> use recursive locks everywhere. I'd definitely be "for" providing

> functions for both lock types!


My idea was indeed that all lock would have to be recursive in a first time and 
differential later if that proves necessary. I'm happy to revisit that though.

>

>>> I have doubts about practical implementation of these functions for

>>> any

>>> RTOS, because of the __LOCK_INIT() macro used for initialization.

>>> In

>>> every retargeted function you'll have to start critical section

>>> (most

>>> likely by disabling interrupts) to initialize the object on heap,

>>> but

>>> then how would you use heap if malloc()'s lock is used via these

>>> functions too?

>>

>> I do not understand. Why would __LOCK_INIT need to start a critical

>> section?

>

> OK, this was not very clear. The problem with simple functions like the

> ones in your patch is that they are extremely hard to actually use in

> an RTOS. Or maybe it just seems so hard for me, that's also possible.

>

> This problem affects only "static" locks created with __LOCK_INIT and

> __LOCK_INIT_RECURSIVE macros. With your patch such locks are just

> uintptr_t with value 0. From my understanding it seems that such locks

> are considered to be "initialized". So you can call __lock_acquire() on

> such object right away. But how would such function look like?

>

> void __lock_acquire(_LOCK_T lock)

> {

> 	if (lock == 0)

> 	{

> 		// lock not yet really initialized...

> 		disableInterrupts();

> 		if (lock == 0)	// re-check due to possible race condition

> 			lock = malloc(sizeof(RealMutexFromYourRtos));

>

> 		assert(lock != 0);

> 		rtos_mutex_create(lock);

> 		enableInterrupts();

> 	}

>

> 	rtos_mutex_lock((RealMutexFromYourRtos*)lock);

> }


Oh I see, that's very clear now, thank you.

>

> This could work, if only malloc() was not protected with the statically

> created _LOCK_T lock itself, which would need such initialization

> too... I don't see any other way to write such function for an RTOS

> which has mutexes with size greater than "uintptr_t" (probably huge

> majority). You could do it with a static pool of storage for mutexes,

> but then you'd have to know upfront how many you want and your RTOS

> would have to allow creation of locks in provided storage (most of them

> allow that, but not all). Maybe I just don't see the simplest solution

> to this problem?


Why do mutex needs to be more than a single integer? How big are mutex in RTOS 
typically in your experience?

>

> Don't get me wrong - I'd really like something like this to be in

> newlib. It just seems that this is extremely hard due to the

> __LOCK_INIT macro which hardcodes the actual size of storage... I'm

> really interested in that feature, as I'd like to provide better newlib

> integration for the RTOS I'm writing ( http://distortos.org/ ).

>

> Regasrds,

> FCh

>


Best regards,

Thomas
onkel.jack@t-online.de Nov. 11, 2016, 11:19 a.m. UTC | #8
To build multithread for running singlethread seems not to make sense to me.
On the other hand, bare metal could implement some threading model without undergoing the pain to put another OS implementation into newlib.
In fact I'm doing so at the moment by patching lock.h (not a good solution through).

I figured out in practice, building newlib for multithread with no locks implemented just lead to a binary that will crash if malloc will be used concurrent. I would consider this as a formal bug first hand.  

So any improvement would be highly welcome.
I will try to build a proposal solution next days. 
Only problem I see there is the typedefs in lock.h.

-----Original-Nachricht-----
Betreff: Re: AW: [PATCH, newlib] Allow locking routine to be retargeted
Datum: 2016-11-11T12:02:15+0100
Von: "Thomas Preudhomme" <thomas.preudhomme@foss.arm.com>
An: "newlib@sourceware.org" <newlib@sourceware.org>



On 11/11/16 09:37, onkel.jack@t-online.de wrote:
> Hello,

>

> to unconditionally provide weak dummy implementation I dont think its a good idea since it has some implications:

> If the newlib is compiled for single thread, no locks are required at all, so the dummy implementation/macro is doing the job to

> satisfy the linker.

> But for multithread its dangerous to provide a dummy since you would not see first hand there are no locks but dummys.


I don't see how is that different from what is happening now. Right now for 
bare-metal targets the locking macro are always void ones.

>

> So my suggestion would be:

> - For single thread leave the implementation as is.

> - For multithread, dont provide any lock function, so linking newlib without to provide locking functions from os/kernel

> would result in a linkage error, therefore forces the user not to forget to provide them.

>

> So I think, just put something like #ifndef MULTI_THREAD around the existing macros would do it.


That would defeat the purpose IMO. The point is to provide a single newlib build 
for both single threaded and multithreaded applications: newlib would be built 
for multithread environment and would work fine for single thread without need 
to define any function and multithread applications (or more likely the RTOS) 
can define locking functions to make it work.

Best regards,

Thomas

Freddie Chopin Nov. 11, 2016, 11:54 a.m. UTC | #9
On Fri, 2016-11-11 at 11:09 +0000, Thomas Preudhomme wrote:
> Why do mutex needs to be more than a single integer? How big are

> mutex in RTOS 

> typically in your experience?


Well, mutexes or things like that may be just a "int" in Linux or other
"big" systems, where they are really just handles for data structures
managed by the kernel. In "small" embedded RTOSes these object usually
contain everything that is needed to implement the complete
functionality, especially the linked list of threads that are blocked
on this object.

In my RTOS mutex is 28 bytes.
In ChibiOS/RT mutex is 20 bytes.
In FreeRTOS mutx is a monster with size of at least 70 bytes (depending
on the config this can be a bit more).

(above number are valid for 32-bit architectures)

Regards,
FCh
Sebastian Huber Nov. 11, 2016, 11:56 a.m. UTC | #10
----- Thomas Preudhomme <thomas.preudhomme@foss.arm.com> schrieb:
> On 10/11/16 20:32, Freddie Chopin wrote:

[...]
> > This could work, if only malloc() was not protected with the statically

> > created _LOCK_T lock itself, which would need such initialization

> > too... I don't see any other way to write such function for an RTOS

> > which has mutexes with size greater than "uintptr_t" (probably huge

> > majority). You could do it with a static pool of storage for mutexes,

> > but then you'd have to know upfront how many you want and your RTOS

> > would have to allow creation of locks in provided storage (most of them

> > allow that, but not all). Maybe I just don't see the simplest solution

> > to this problem?

> 

> Why do mutex needs to be more than a single integer? How big are mutex in RTOS 

> typically in your experience?


This depends on what features the RTOS wants to support.  For example SMP, locking protocols, blocking on a wait queue, user-provided or external-provided storage, nesting, etc.

For RTEMS a recursive mutex with support for priority inheritance (or OMIP) and SMP (optional) needs 20 bytes on a 32-bit machine for example:

https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=newlib/libc/sys/rtems/include/sys/lock.h;h=e0d77cb614b348a949e23ca4c543b1ce6a055b6d;hb=HEAD#l55

With an integer only you can implement a futex like on Linux, however, this needs a complex infrastructure in the background and provides only random fairness.

-- 
Sebastian Huber, embedded brains GmbH

Address : Dornierstr. 4, D-82178 Puchheim, Germany
Phone   : +49 89 189 47 41-16
Fax     : +49 89 189 47 41-09
E-Mail  : sebastian.huber at embedded-brains.de
PGP     : Public key available on request.

Diese Nachricht ist keine geschäftliche Mitteilung im Sinne des EHUG.
Freddie Chopin Nov. 11, 2016, 12:13 p.m. UTC | #11
On Fri, 2016-11-11 at 12:19 +0100, onkel.jack@t-online.de wrote:
> To build multithread for running singlethread seems not to make sense

> to me.


I think that the only thing that is changed with the "multithreaded"
enable switch are the locks, so with this change you could really use
"multithreaded" variant of newlib for singlethreaded application with
no issues (apart from a minor size increase).

> On the other hand, bare metal could implement some threading model

> without undergoing the pain to put another OS implementation into

> newlib.

> In fact I'm doing so at the moment by patching lock.h (not a good

> solution through).


Yes, that would be great indeed. I had a plan to just drop whole newlib
into my RTOS and patch lock.h for my needs.

> I figured out in practice, building newlib for multithread with no

> locks implemented just lead to a binary that will crash if malloc

> will be used concurrent. I would consider this as a formal bug first

> hand.  


This is not entirely true. In case of malloc (and also time-zone info)
you don't actually need to have proper lock.h. All you need to do is to
reimplement __malloc_lock() and __malloc_unlock() functions. This is
what I've done in my RTOS for now and this works well. The mutex used
by these two functions is a global object initialized manually before
main() starts (and before global constructors).

https://github.com/DISTORTEC/distortos/blob/master/source/syscalls/malloc_unlock.cpp#L27
https://github.com/DISTORTEC/distortos/blob/master/source/syscalls/malloc_unlock.cpp#L27
https://github.com/DISTORTEC/distortos/blob/master/source/memory/getMallocMutex.cpp#L38
https://github.com/DISTORTEC/distortos/blob/master/source/scheduler/lowLevelInitialization.cpp#L106

This has a "small" problem, that it only makes malloc() and free()
usable in multithreaded scenario, but leaves everything else vulnerable
(like FILE objects)...

> So any improvement would be highly welcome.

> I will try to build a proposal solution next days. 

> Only problem I see there is the typedefs in lock.h.


I think that if _ALL_ locks in newlib would be created with functions -
so with __lock_init() instead of the _LOCK_INIT() macro - this would
make the implementation of such overrides much simpler. On the other
hand you'd have to somehow call initialization functions for global
locks before your application starts...

The other viable option is to leave "dynamic" locks as is (so the lock
created with __lock_init() stay exactly as now) and provide functions
like __malloc_lock() / __malloc_unlock() for _ALL_ global locks. This
way the typedefs in newlib could be a pointer and using these functions
for global locks would allow them to be overriden in your project. From
a quick look it seems there are 8 global locks in newlib (telldir, 2x
findfp, time-zone info, malloc, atexit, environment, quick atexit). For
two of them we already have such functions (malloc and time-zone info).

Regards,
FCh
Thomas Preudhomme Nov. 11, 2016, 1:40 p.m. UTC | #12
On 11/11/16 11:56, Sebastian Huber wrote:
>

> ----- Thomas Preudhomme <thomas.preudhomme@foss.arm.com> schrieb:

>> On 10/11/16 20:32, Freddie Chopin wrote:

> [...]

>>> This could work, if only malloc() was not protected with the statically

>>> created _LOCK_T lock itself, which would need such initialization

>>> too... I don't see any other way to write such function for an RTOS

>>> which has mutexes with size greater than "uintptr_t" (probably huge

>>> majority). You could do it with a static pool of storage for mutexes,

>>> but then you'd have to know upfront how many you want and your RTOS

>>> would have to allow creation of locks in provided storage (most of them

>>> allow that, but not all). Maybe I just don't see the simplest solution

>>> to this problem?

>>

>> Why do mutex needs to be more than a single integer? How big are mutex in RTOS

>> typically in your experience?

>

> This depends on what features the RTOS wants to support.  For example SMP, locking protocols, blocking on a wait queue, user-provided or external-provided storage, nesting, etc.

>

> For RTEMS a recursive mutex with support for priority inheritance (or OMIP) and SMP (optional) needs 20 bytes on a 32-bit machine for example:

>

> https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=newlib/libc/sys/rtems/include/sys/lock.h;h=e0d77cb614b348a949e23ca4c543b1ce6a055b6d;hb=HEAD#l55


Right, I didn't think of extra features. The one thing I could think of would be 
to remove usage of the static initializers and only use the function initializer 
which could do a malloc if needed. The issue you described with malloc can be 
avoided by redefining malloc_lock so as not to call the generic lock function.

Now whether this is worth the trouble is another issue because that would 
require more work. Thanks a lot for the feedback, I'm glad you pointed out the 
pitfalls in that approach. It is time to go back to the drawing board.

Best regards,

Thomas
Freddie Chopin Nov. 11, 2016, 2:09 p.m. UTC | #13
On Fri, 2016-11-11 at 13:40 +0000, Thomas Preudhomme wrote:
> Right, I didn't think of extra features. The one thing I could think

> of would be 

> to remove usage of the static initializers and only use the function

> initializer 

> which could do a malloc if needed. The issue you described with

> malloc can be 

> avoided by redefining malloc_lock so as not to call the generic lock

> function.

> 

> Now whether this is worth the trouble is another issue because that

> would 

> require more work. Thanks a lot for the feedback, I'm glad you

> pointed out the 

> pitfalls in that approach. It is time to go back to the drawing

> board.


See my earlier message to onkel.jack - I've given some alternative
options there at the end, the one you mentioned is also there. It has a
major problem, that it's not easily possible to actually execute these
initialization functions for most of the global locks...

https://sourceware.org/ml/newlib/2016/msg01093.html

Regards,
FCh
Thomas Preudhomme Nov. 23, 2016, 2:38 p.m. UTC | #14
Hi Sebastian,

I was thinking on how to solve this issue and found two possible solutions:

1) select size of lock at configure time

Instead of being an enable/disable option the configure option could take an 
integer value N that determine the size of lock, default being the size of a 
pointer. The lock would then be defined as an array of N integers.

Pro: LOCK_INIT works, simpler & smaller change
Cons: lock needs to be as big as the biggest lock among all the platforms you 
want to target wasting a bit of space.

2) Remove static initialization of locks

Remove all LOCK_INIT and add code as required to call lock_initialize at start 
up time.

Pro: you only pay the size for the lock you need (ie nothing except empty 
functions in the single threaded case)
Cons: much bigger work, start up cannot support multithread


I'm more in favor of 1) because that's good enough for us and the change is 
smaller in scale but I want to make sure that's good for your use case as well.

Best regards,

Thomas

On 11/11/16 11:56, Sebastian Huber wrote:
>

> ----- Thomas Preudhomme <thomas.preudhomme@foss.arm.com> schrieb:

>> On 10/11/16 20:32, Freddie Chopin wrote:

> [...]

>>> This could work, if only malloc() was not protected with the statically

>>> created _LOCK_T lock itself, which would need such initialization

>>> too... I don't see any other way to write such function for an RTOS

>>> which has mutexes with size greater than "uintptr_t" (probably huge

>>> majority). You could do it with a static pool of storage for mutexes,

>>> but then you'd have to know upfront how many you want and your RTOS

>>> would have to allow creation of locks in provided storage (most of them

>>> allow that, but not all). Maybe I just don't see the simplest solution

>>> to this problem?

>>

>> Why do mutex needs to be more than a single integer? How big are mutex in RTOS

>> typically in your experience?

>

> This depends on what features the RTOS wants to support.  For example SMP, locking protocols, blocking on a wait queue, user-provided or external-provided storage, nesting, etc.

>

> For RTEMS a recursive mutex with support for priority inheritance (or OMIP) and SMP (optional) needs 20 bytes on a 32-bit machine for example:

>

> https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=newlib/libc/sys/rtems/include/sys/lock.h;h=e0d77cb614b348a949e23ca4c543b1ce6a055b6d;hb=HEAD#l55

>

> With an integer only you can implement a futex like on Linux, however, this needs a complex infrastructure in the background and provides only random fairness.

>
Freddie Chopin Nov. 23, 2016, 3:51 p.m. UTC | #15
Hi!

I'm not Sebastian, but I took the liberty to give an opinion here.
Option 1 brings almost nothing, as you have to make sure the toolchain
you use supports the RTOS you want to use. If it doesn't you have to
build your own. If you have to build your own, then you can as well
just integrate locks without this change...

As for the option 2 I guess that it would be perfectly reasonable to
declare some kind of startup function that should be called by user's
RTOS.

I have just thought about another idea:
1. Newlib would define - in a public header - number of required
recursive and non-recursive statically allocated locks:
#define AMOUNT_OF_STATIC_LOCKS 5
#define AMOUNT_OF_STATIC_RECURSIVE_LOCKS 3
2. In the same header it would define functions like "void*
get_lock(size_t index)" and "void* get_recursive_lock(size_t index)".
3. In some private newlib header there would be assignments of indexes
with locks like:
#define MALLOC_RECURSIVE_LOCK 0
#define STDIO_RECURSIVE_LOCK 1
...
#define TZ_LOCK 0
#define WHATEVER_LOCK 1
...
4. All uses of locks like:
__lock_acquire_recursive(__malloc_lock);
would have to be changed to:
__lock_acquire_recursive(get_recursive_lock(MALLOC_RECURSIVE_LOCK));


This way the user could just have in his own RTOS a nice array of locks
with proper size and automatic amount of elements. More advantages:
- by having two functions and two defines we could easily have
recursive and non-recursive locks;
- we could just drop __LOCK_INIT and add a requirement for user to
initialize the locks;

What do you think?

Regards,
FCh
Sebastian Huber Nov. 24, 2016, 7:44 a.m. UTC | #16
Hello Thomas,

On 23/11/16 15:38, Thomas Preudhomme wrote:
> Hi Sebastian,

>

> I was thinking on how to solve this issue and found two possible 

> solutions:

>

> 1) select size of lock at configure time

>

> Instead of being an enable/disable option the configure option could 

> take an integer value N that determine the size of lock, default being 

> the size of a pointer. The lock would then be defined as an array of N 

> integers.

>

> Pro: LOCK_INIT works, simpler & smaller change

> Cons: lock needs to be as big as the biggest lock among all the 

> platforms you want to target wasting a bit of space.

>

> 2) Remove static initialization of locks

>

> Remove all LOCK_INIT and add code as required to call lock_initialize 

> at start up time.

>

> Pro: you only pay the size for the lock you need (ie nothing except 

> empty functions in the single threaded case)

> Cons: much bigger work, start up cannot support multithread


3)

struct _lock;

typedef struct _lock *_LOCK_T;

#define __LOCK_INIT(class, lock) extern struct _lock _lock_ ## lock; class _LOCK_T lock = &_lock_ ## lock;

The OS must then provide struct _lock and storage for all statically initialized locks.

-- 
Sebastian Huber, embedded brains GmbH

Address : Dornierstr. 4, D-82178 Puchheim, Germany
Phone   : +49 89 189 47 41-16
Fax     : +49 89 189 47 41-09
E-Mail  : sebastian.huber@embedded-brains.de
PGP     : Public key available on request.

Diese Nachricht ist keine geschäftliche Mitteilung im Sinne des EHUG.
Freddie Chopin Nov. 24, 2016, 8:29 a.m. UTC | #17
On Thu, 2016-11-24 at 08:44 +0100, Sebastian Huber wrote:
> 3)

> 

> struct _lock;

> 

> typedef struct _lock *_LOCK_T;

> 

> #define __LOCK_INIT(class, lock) extern struct _lock _lock_ ## lock;

> class _LOCK_T lock = &_lock_ ## lock;

> 

> The OS must then provide struct _lock and storage for all statically

> initialized locks.


Good and simple, changes almost nothing in newlib's code, easy to have
both recursive and non-recursive locks.

+1

I think it would be possible to get rid of the "class _LOCK_T lock =
&_lock_ ## lock;" part. For example with macros like:

#define __tz_lock_object &__tz_lock_storage

Regards,
FCh
Thomas Preudhomme Nov. 25, 2016, 10:21 a.m. UTC | #18
On 24/11/16 07:44, Sebastian Huber wrote:
> Hello Thomas,

>

> On 23/11/16 15:38, Thomas Preudhomme wrote:

>> Hi Sebastian,

>>

>> I was thinking on how to solve this issue and found two possible solutions:

>>

>> 1) select size of lock at configure time

>>

>> Instead of being an enable/disable option the configure option could take an

>> integer value N that determine the size of lock, default being the size of a

>> pointer. The lock would then be defined as an array of N integers.

>>

>> Pro: LOCK_INIT works, simpler & smaller change

>> Cons: lock needs to be as big as the biggest lock among all the platforms you

>> want to target wasting a bit of space.

>>

>> 2) Remove static initialization of locks

>>

>> Remove all LOCK_INIT and add code as required to call lock_initialize at start

>> up time.

>>

>> Pro: you only pay the size for the lock you need (ie nothing except empty

>> functions in the single threaded case)

>> Cons: much bigger work, start up cannot support multithread

>

> 3)

>

> struct _lock;

>

> typedef struct _lock *_LOCK_T;

>

> #define __LOCK_INIT(class, lock) extern struct _lock _lock_ ## lock; class

> _LOCK_T lock = &_lock_ ## lock;

>

> The OS must then provide struct _lock and storage for all statically initialized

> locks.


Oh nice, clever. I've check whether there is name collision in the source where 
this would lead them to use the same lock and found:

newlib/libc/sys/linux/iconv/gconv_db.c: __LOCK_INIT(static, lock);
newlib/libc/sys/linux/iconv/gconv_trans.c: __LOCK_INIT(static, lock);

The one thing I worry is the risk of new lock being added with the same name. 
Fortunately this could be detected at link time by looking for relocation to 
"_lock_.*" in newlib library and check that there is no duplicate.

Best regards,

Thomas
Thomas Preudhomme Nov. 25, 2016, 12:04 p.m. UTC | #19
On 25/11/16 10:21, Thomas Preudhomme wrote:
>

>

> On 24/11/16 07:44, Sebastian Huber wrote:

>> Hello Thomas,

>>

>> On 23/11/16 15:38, Thomas Preudhomme wrote:

>>> Hi Sebastian,

>>>

>>> I was thinking on how to solve this issue and found two possible solutions:

>>>

>>> 1) select size of lock at configure time

>>>

>>> Instead of being an enable/disable option the configure option could take an

>>> integer value N that determine the size of lock, default being the size of a

>>> pointer. The lock would then be defined as an array of N integers.

>>>

>>> Pro: LOCK_INIT works, simpler & smaller change

>>> Cons: lock needs to be as big as the biggest lock among all the platforms you

>>> want to target wasting a bit of space.

>>>

>>> 2) Remove static initialization of locks

>>>

>>> Remove all LOCK_INIT and add code as required to call lock_initialize at start

>>> up time.

>>>

>>> Pro: you only pay the size for the lock you need (ie nothing except empty

>>> functions in the single threaded case)

>>> Cons: much bigger work, start up cannot support multithread

>>

>> 3)

>>

>> struct _lock;

>>

>> typedef struct _lock *_LOCK_T;

>>

>> #define __LOCK_INIT(class, lock) extern struct _lock _lock_ ## lock; class

>> _LOCK_T lock = &_lock_ ## lock;

>>

>> The OS must then provide struct _lock and storage for all statically initialized

>> locks.

>

> Oh nice, clever. I've check whether there is name collision in the source where

> this would lead them to use the same lock and found:

>

> newlib/libc/sys/linux/iconv/gconv_db.c: __LOCK_INIT(static, lock);

> newlib/libc/sys/linux/iconv/gconv_trans.c: __LOCK_INIT(static, lock);

>

> The one thing I worry is the risk of new lock being added with the same name.

> Fortunately this could be detected at link time by looking for relocation to

> "_lock_.*" in newlib library and check that there is no duplicate.


One issue though, this means the OS needs to be updated each time a statically 
initialized lock is added to newlib. That seems to be a bit too much coupling. 
Or am I misunderstanding how this should be used?

Best regards,

Thomas
Sebastian Huber Nov. 25, 2016, 12:35 p.m. UTC | #20
On 25/11/16 13:04, Thomas Preudhomme wrote:
>

>

> On 25/11/16 10:21, Thomas Preudhomme wrote:

>>

>>

>> On 24/11/16 07:44, Sebastian Huber wrote:

>>> Hello Thomas,

>>>

>>> On 23/11/16 15:38, Thomas Preudhomme wrote:

>>>> Hi Sebastian,

>>>>

>>>> I was thinking on how to solve this issue and found two possible 

>>>> solutions:

>>>>

>>>> 1) select size of lock at configure time

>>>>

>>>> Instead of being an enable/disable option the configure option 

>>>> could take an

>>>> integer value N that determine the size of lock, default being the 

>>>> size of a

>>>> pointer. The lock would then be defined as an array of N integers.

>>>>

>>>> Pro: LOCK_INIT works, simpler & smaller change

>>>> Cons: lock needs to be as big as the biggest lock among all the 

>>>> platforms you

>>>> want to target wasting a bit of space.

>>>>

>>>> 2) Remove static initialization of locks

>>>>

>>>> Remove all LOCK_INIT and add code as required to call 

>>>> lock_initialize at start

>>>> up time.

>>>>

>>>> Pro: you only pay the size for the lock you need (ie nothing except 

>>>> empty

>>>> functions in the single threaded case)

>>>> Cons: much bigger work, start up cannot support multithread

>>>

>>> 3)

>>>

>>> struct _lock;

>>>

>>> typedef struct _lock *_LOCK_T;

>>>

>>> #define __LOCK_INIT(class, lock) extern struct _lock _lock_ ## lock; 

>>> class

>>> _LOCK_T lock = &_lock_ ## lock;

>>>

>>> The OS must then provide struct _lock and storage for all statically 

>>> initialized

>>> locks.

>>

>> Oh nice, clever. I've check whether there is name collision in the 

>> source where

>> this would lead them to use the same lock and found:

>>

>> newlib/libc/sys/linux/iconv/gconv_db.c: __LOCK_INIT(static, lock);

>> newlib/libc/sys/linux/iconv/gconv_trans.c: __LOCK_INIT(static, lock);

>>

>> The one thing I worry is the risk of new lock being added with the 

>> same name.

>> Fortunately this could be detected at link time by looking for 

>> relocation to

>> "_lock_.*" in newlib library and check that there is no duplicate.

>

> One issue though, this means the OS needs to be updated each time a 

> statically initialized lock is added to newlib. That seems to be a bit 

> too much coupling. Or am I misunderstanding how this should be used?


Yes, but how many locks per minute are added to Newlib?

-- 
Sebastian Huber, embedded brains GmbH

Address : Dornierstr. 4, D-82178 Puchheim, Germany
Phone   : +49 89 189 47 41-16
Fax     : +49 89 189 47 41-09
E-Mail  : sebastian.huber@embedded-brains.de
PGP     : Public key available on request.

Diese Nachricht ist keine geschäftliche Mitteilung im Sinne des EHUG.
Freddie Chopin Nov. 25, 2016, 2:12 p.m. UTC | #21
On Fri, 2016-11-25 at 12:04 +0000, Thomas Preudhomme wrote:
> > Oh nice, clever. I've check whether there is name collision in the

> > source where

> > this would lead them to use the same lock and found:

> > 

> > newlib/libc/sys/linux/iconv/gconv_db.c: __LOCK_INIT(static, lock);

> > newlib/libc/sys/linux/iconv/gconv_trans.c: __LOCK_INIT(static,

> > lock);

> > 

> > The one thing I worry is the risk of new lock being added with the

> > same name.

> > Fortunately this could be detected at link time by looking for

> > relocation to

> > "_lock_.*" in newlib library and check that there is no duplicate.

> 

> One issue though, this means the OS needs to be updated each time a

> statically 

> initialized lock is added to newlib. That seems to be a bit too much

> coupling. 

> Or am I misunderstanding how this should be used?


The solution with array and function - which is basically very similar
to this one - solves both these issues. But this has a downside of
exposing all the locks, even if they are considered to be file-local
"static" in newlib.

The second issue in Sebastian's solution can also be solved with a
special header - a source file fragment really - with declaration of
storage for all locks. Sth like:

struct _lock _lock_malloc;
struct _lock _lock_tz;
struct _lock _lock_whatever;
struct _lock _lock_xxx;

The OS would then just need to include this file _AFTER_ the _lock
struct is defined. And you would see all name collisions then.

Regards,
FCh
Thomas Preudhomme Dec. 13, 2016, 5:20 p.m. UTC | #22
Hi Sebastian,

I've just followed your comments and respinned[1] the patch to enable 
retargetable locking routines in newlib. Hopes this one will be useful to you as 
well.

[1] https://sourceware.org/ml/newlib/2016/msg01165.html

Best regards,

Thomas



On 25/11/16 12:35, Sebastian Huber wrote:
>

>

> On 25/11/16 13:04, Thomas Preudhomme wrote:

>>

>>

>> On 25/11/16 10:21, Thomas Preudhomme wrote:

>>>

>>>

>>> On 24/11/16 07:44, Sebastian Huber wrote:

>>>> Hello Thomas,

>>>>

>>>> On 23/11/16 15:38, Thomas Preudhomme wrote:

>>>>> Hi Sebastian,

>>>>>

>>>>> I was thinking on how to solve this issue and found two possible solutions:

>>>>>

>>>>> 1) select size of lock at configure time

>>>>>

>>>>> Instead of being an enable/disable option the configure option could take an

>>>>> integer value N that determine the size of lock, default being the size of a

>>>>> pointer. The lock would then be defined as an array of N integers.

>>>>>

>>>>> Pro: LOCK_INIT works, simpler & smaller change

>>>>> Cons: lock needs to be as big as the biggest lock among all the platforms you

>>>>> want to target wasting a bit of space.

>>>>>

>>>>> 2) Remove static initialization of locks

>>>>>

>>>>> Remove all LOCK_INIT and add code as required to call lock_initialize at start

>>>>> up time.

>>>>>

>>>>> Pro: you only pay the size for the lock you need (ie nothing except empty

>>>>> functions in the single threaded case)

>>>>> Cons: much bigger work, start up cannot support multithread

>>>>

>>>> 3)

>>>>

>>>> struct _lock;

>>>>

>>>> typedef struct _lock *_LOCK_T;

>>>>

>>>> #define __LOCK_INIT(class, lock) extern struct _lock _lock_ ## lock; class

>>>> _LOCK_T lock = &_lock_ ## lock;

>>>>

>>>> The OS must then provide struct _lock and storage for all statically

>>>> initialized

>>>> locks.

>>>

>>> Oh nice, clever. I've check whether there is name collision in the source where

>>> this would lead them to use the same lock and found:

>>>

>>> newlib/libc/sys/linux/iconv/gconv_db.c: __LOCK_INIT(static, lock);

>>> newlib/libc/sys/linux/iconv/gconv_trans.c: __LOCK_INIT(static, lock);

>>>

>>> The one thing I worry is the risk of new lock being added with the same name.

>>> Fortunately this could be detected at link time by looking for relocation to

>>> "_lock_.*" in newlib library and check that there is no duplicate.

>>

>> One issue though, this means the OS needs to be updated each time a statically

>> initialized lock is added to newlib. That seems to be a bit too much coupling.

>> Or am I misunderstanding how this should be used?

>

> Yes, but how many locks per minute are added to Newlib?

>
diff mbox

Patch

diff --git a/newlib/configure b/newlib/configure
index 30e1d57664302248af9087b0051290e2aff4b382..42b1e13feeda8a5b7ad3cf365393bd4640da4c8c 100755
--- a/newlib/configure
+++ b/newlib/configure
@@ -798,6 +798,7 @@  enable_newlib_nano_malloc
 enable_newlib_unbuf_stream_opt
 enable_lite_exit
 enable_newlib_nano_formatted_io
+enable_newlib_retargetable_locking
 enable_multilib
 enable_target_optspace
 enable_malloc_debugging
@@ -1469,6 +1470,7 @@  Optional Features:
   --disable-newlib-unbuf-stream-opt    disable unbuffered stream optimization in streamio
   --enable-lite-exit	enable light weight exit
   --enable-newlib-nano-formatted-io    Use nano version formatted IO
+  --enable-newlib-retargetable-locking    Allow locking routines to be retargeted at link time
   --enable-multilib         build many library versions (default)
   --enable-target-optspace  optimize for space
   --enable-malloc-debugging indicate malloc debugging requested
@@ -2470,6 +2472,18 @@  else
 fi
 
 
+# Check whether --enable-newlib-retargetable-locking was given.
+if test "${enable_newlib_retargetable_locking+set}" = set; then :
+  enableval=$enable_newlib_retargetable_locking; case "${enableval}" in
+   yes) newlib_retargetable_locking=yes ;;
+   no)  newlib_retargetable_locking=no ;;
+   *) as_fn_error $? "bad value ${enableval} for newlib-retargetable-locking" "$LINENO" 5 ;;
+ esac
+else
+  newlib_retargetable_locking=no
+fi
+
+
 
 # Make sure we can run config.sub.
 $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
@@ -11776,7 +11790,7 @@  else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11779 "configure"
+#line 11793 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11882,7 +11896,7 @@  else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11885 "configure"
+#line 11899 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12421,6 +12435,13 @@  _ACEOF
 
 fi
 
+if test "${newlib_retargetable_locking}" = "yes"; then
+cat >>confdefs.h <<_ACEOF
+#define _RETARGETABLE_LOCKING 1
+_ACEOF
+
+fi
+
 
 if test "x${iconv_encodings}" != "x" \
    || test "x${iconv_to_encodings}" != "x" \
diff --git a/newlib/configure.in b/newlib/configure.in
index 01c6367a99cdf6f74a5ad15c8a4eb4a45f206e9d..70cffa76a966f8d9cd1c8888156002e76ea13dc2 100644
--- a/newlib/configure.in
+++ b/newlib/configure.in
@@ -218,6 +218,17 @@  AC_ARG_ENABLE(newlib_nano_formatted_io,
    *) AC_MSG_ERROR(bad value ${enableval} for newlib-nano-formatted-io) ;;
  esac],[newlib_nano_formatted_io=no])
 
+dnl Support --enable-retargetable-locking
+dnl This option is also read in libc/configure.in.  It is repeated
+dnl here so that it shows up in the help text.
+AC_ARG_ENABLE(newlib-retargetable-locking,
+[  --enable-newlib-retargetable-locking    Allow locking routines to be retargeted at link time],
+[case "${enableval}" in
+   yes) newlib_retargetable_locking=yes ;;
+   no)  newlib_retargetable_locking=no ;;
+   *) AC_MSG_ERROR(bad value ${enableval} for newlib-retargetable-locking) ;;
+ esac],[newlib_retargetable_locking=no])
+
 NEWLIB_CONFIGURE(.)
 
 dnl We have to enable libtool after NEWLIB_CONFIGURE because if we try and
@@ -442,6 +453,10 @@  if test "${newlib_nano_formatted_io}" = "yes"; then
 AC_DEFINE_UNQUOTED(_NANO_FORMATTED_IO)
 fi
 
+if test "${newlib_retargetable_locking}" = "yes"; then
+AC_DEFINE_UNQUOTED(_RETARGETABLE_LOCKING)
+fi
+
 dnl
 dnl Parse --enable-newlib-iconv-encodings option argument
 dnl
diff --git a/newlib/libc/configure b/newlib/libc/configure
index 5dccc852b8cd90d697a90f113ec17982741ecc3a..35077b81550aa999026ff67a7428fec6509a26ce 100755
--- a/newlib/libc/configure
+++ b/newlib/libc/configure
@@ -751,6 +751,8 @@  build
 newlib_basedir
 MAY_SUPPLY_SYSCALLS_FALSE
 MAY_SUPPLY_SYSCALLS_TRUE
+NEWLIB_RETARGETABLE_LOCKING_FALSE
+NEWLIB_RETARGETABLE_LOCKING_TRUE
 NEWLIB_NANO_FORMATTED_IO_FALSE
 NEWLIB_NANO_FORMATTED_IO_TRUE
 target_alias
@@ -797,6 +799,7 @@  enable_option_checking
 enable_newlib_io_pos_args
 enable_newlib_nano_malloc
 enable_newlib_nano_formatted_io
+enable_newlib_retargetable_locking
 enable_multilib
 enable_target_optspace
 enable_malloc_debugging
@@ -1448,6 +1451,7 @@  Optional Features:
   --enable-newlib-io-pos-args enable printf-family positional arg support
   --enable-newlib-nano-malloc    Use small-footprint nano-malloc implementation
   --enable-newlib-nano-formatted-io    Use small-footprint nano-formatted-IO implementation
+  --enable-newlib-retargetable-locking    Allow locking routines to be retargeted at link time
   --enable-multilib         build many library versions (default)
   --enable-target-optspace  optimize for space
   --enable-malloc-debugging indicate malloc debugging requested
@@ -2252,6 +2256,26 @@  else
 fi
 
 
+# Check whether --enable-newlib-retargetable-locking was given.
+if test "${enable_newlib_retargetable_locking+set}" = set; then :
+  enableval=$enable_newlib_retargetable_locking; case "${enableval}" in
+   yes) newlib_retargetable_locking=yes ;;
+   no)  newlib_retargetable_lock=no ;;
+   *) as_fn_error $? "bad value ${enableval} for newlib-retargetable-locking" "$LINENO" 5 ;;
+ esac
+else
+  newlib_retargetable_locking=no
+fi
+
+ if test x$newlib_retargetable_locking = xyes; then
+  NEWLIB_RETARGETABLE_LOCKING_TRUE=
+  NEWLIB_RETARGETABLE_LOCKING_FALSE='#'
+else
+  NEWLIB_RETARGETABLE_LOCKING_TRUE='#'
+  NEWLIB_RETARGETABLE_LOCKING_FALSE=
+fi
+
+
 
 # Make sure we can run config.sub.
 $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
@@ -11525,7 +11549,7 @@  else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11528 "configure"
+#line 11552 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -11631,7 +11655,7 @@  else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 11634 "configure"
+#line 11658 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12248,6 +12272,10 @@  if test -z "${NEWLIB_NANO_FORMATTED_IO_TRUE}" && test -z "${NEWLIB_NANO_FORMATTE
   as_fn_error $? "conditional \"NEWLIB_NANO_FORMATTED_IO\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${NEWLIB_RETARGETABLE_LOCKING_TRUE}" && test -z "${NEWLIB_RETARGETABLE_LOCKING_FALSE}"; then
+  as_fn_error $? "conditional \"NEWLIB_RETARGETABLE_LOCKING\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${MAY_SUPPLY_SYSCALLS_TRUE}" && test -z "${MAY_SUPPLY_SYSCALLS_FALSE}"; then
   as_fn_error $? "conditional \"MAY_SUPPLY_SYSCALLS\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
diff --git a/newlib/libc/configure.in b/newlib/libc/configure.in
index 0a7bb8815be44fb1e92f753fa2c8df2a31b95f94..ac25a3933c4a4794f3cfe291e0275d0ed001b96a 100644
--- a/newlib/libc/configure.in
+++ b/newlib/libc/configure.in
@@ -36,6 +36,16 @@  AC_ARG_ENABLE(newlib_nano_formatted_io,
  esac],[newlib_nano_formatted_io=no])
 AM_CONDITIONAL(NEWLIB_NANO_FORMATTED_IO, test x$newlib_nano_formatted_io = xyes)
 
+dnl Support --enable-retargetable-locking used by libc/sys
+AC_ARG_ENABLE(newlib-retargetable-locking,
+[  --enable-newlib-retargetable-locking    Allow locking routines to be retargeted at link time],
+[case "${enableval}" in
+   yes) newlib_retargetable_locking=yes ;;
+   no)  newlib_retargetable_lock=no ;;
+   *) AC_MSG_ERROR(bad value ${enableval} for newlib-retargetable-locking) ;;
+ esac],[newlib_retargetable_locking=no])
+AM_CONDITIONAL(NEWLIB_RETARGETABLE_LOCKING, test x$newlib_retargetable_locking = xyes)
+
 NEWLIB_CONFIGURE(..)
 
 AM_CONDITIONAL(NEWLIB_NANO_MALLOC, test x$newlib_nano_malloc = xyes)
diff --git a/newlib/libc/include/sys/lock.h b/newlib/libc/include/sys/lock.h
index 9075e35c9179968031010432515e3a845ff6ca8d..2578f303c8e365bec1009e8614f207823ff78eb3 100644
--- a/newlib/libc/include/sys/lock.h
+++ b/newlib/libc/include/sys/lock.h
@@ -3,10 +3,13 @@ 
 
 /* dummy lock routines for single-threaded aps */
 
+#include <newlib.h>
+#include <_ansi.h>
+
+#ifndef _RETARGETABLE_LOCKING
+
 typedef int _LOCK_T;
 typedef int _LOCK_RECURSIVE_T;
- 
-#include <_ansi.h>
 
 #define __LOCK_INIT(class,lock) static int lock = 0;
 #define __LOCK_INIT_RECURSIVE(class,lock) static int lock = 0;
@@ -21,4 +24,26 @@  typedef int _LOCK_RECURSIVE_T;
 #define __lock_release(lock) (_CAST_VOID 0)
 #define __lock_release_recursive(lock) (_CAST_VOID 0)
 
+#else
+
+#include <stdint.h>
+
+typedef uintptr_t _LOCK_T;
+#define _LOCK_RECURSIVE_T _LOCK_T
+
+#define __LOCK_INIT(class,lock) class _LOCK_T lock = 0;
+#define __LOCK_INIT_RECURSIVE(class,lock) __LOCK_INIT(class,lock)
+extern void __lock_init(_LOCK_T lock);
+#define __lock_init_recursive(lock) __lock_init(lock)
+extern void __lock_close(_LOCK_T lock);
+#define __lock_close_recursive(lock) __lock_close(lock)
+extern void __lock_acquire(_LOCK_T lock);
+#define __lock_acquire_recursive(lock) __lock_acquire(lock)
+extern void __lock_try_acquire(_LOCK_T lock);
+#define __lock_try_acquire_recursive(lock) __lock_try_acquire(lock)
+extern void __lock_release(_LOCK_T lock);
+#define __lock_release_recursive(lock) __lock_release(lock)
+
+#endif /* _RETARGETABLE_LOCKING */
+
 #endif /* __SYS_LOCK_H__ */
diff --git a/newlib/libc/stdlib/Makefile.am b/newlib/libc/stdlib/Makefile.am
index 2d45d1029ace8087b57657cbc11feabf76f4206e..76ed8480c28ebfb68e2f48432c0359b11f1522a8 100644
--- a/newlib/libc/stdlib/Makefile.am
+++ b/newlib/libc/stdlib/Makefile.am
@@ -74,6 +74,11 @@  GENERAL_SOURCES += \
 	wcstold.c
 endif # HAVE_LONG_DOUBLE
 
+if NEWLIB_RETARGETABLE_LOCKING
+GENERAL_SOURCES += \
+	lock.c
+endif
+
 if NEWLIB_NANO_MALLOC
 MALIGNR=nano-malignr
 MALLOPTR=nano-malloptr
diff --git a/newlib/libc/stdlib/Makefile.in b/newlib/libc/stdlib/Makefile.in
index 466ab6d1e02ea919f6c8ceed792024955bef1a66..3295e981c87bac3b21a6b72dda148f892fe018d6 100644
--- a/newlib/libc/stdlib/Makefile.in
+++ b/newlib/libc/stdlib/Makefile.in
@@ -57,6 +57,9 @@  host_triplet = @host@
 @HAVE_LONG_DOUBLE_TRUE@	strtold.c \
 @HAVE_LONG_DOUBLE_TRUE@	wcstold.c
 
+@NEWLIB_RETARGETABLE_LOCKING_TRUE@am__append_2 = \
+@NEWLIB_RETARGETABLE_LOCKING_TRUE@	lock.c
+
 DIST_COMMON = $(srcdir)/../../Makefile.shared $(srcdir)/Makefile.in \
 	$(srcdir)/Makefile.am
 subdir = stdlib
@@ -81,7 +84,9 @@  lib_a_AR = $(AR) $(ARFLAGS)
 @ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_TRUE@	$(ELIX_2_OBJS)
 @HAVE_LONG_DOUBLE_TRUE@am__objects_1 = lib_a-strtold.$(OBJEXT) \
 @HAVE_LONG_DOUBLE_TRUE@	lib_a-wcstold.$(OBJEXT)
-am__objects_2 = lib_a-__adjust.$(OBJEXT) lib_a-__atexit.$(OBJEXT) \
+@NEWLIB_RETARGETABLE_LOCKING_TRUE@am__objects_2 =  \
+@NEWLIB_RETARGETABLE_LOCKING_TRUE@	lib_a-lock.$(OBJEXT)
+am__objects_3 = lib_a-__adjust.$(OBJEXT) lib_a-__atexit.$(OBJEXT) \
 	lib_a-__call_atexit.$(OBJEXT) lib_a-__exp10.$(OBJEXT) \
 	lib_a-__ten_mu.$(OBJEXT) lib_a-_Exit.$(OBJEXT) \
 	lib_a-abort.$(OBJEXT) lib_a-abs.$(OBJEXT) \
@@ -112,8 +117,8 @@  am__objects_2 = lib_a-__adjust.$(OBJEXT) lib_a-__atexit.$(OBJEXT) \
 	lib_a-wcstol.$(OBJEXT) lib_a-wcstoul.$(OBJEXT) \
 	lib_a-wcstombs.$(OBJEXT) lib_a-wcstombs_r.$(OBJEXT) \
 	lib_a-wctomb.$(OBJEXT) lib_a-wctomb_r.$(OBJEXT) \
-	$(am__objects_1)
-am__objects_3 = lib_a-arc4random.$(OBJEXT) \
+	$(am__objects_1) $(am__objects_2)
+am__objects_4 = lib_a-arc4random.$(OBJEXT) \
 	lib_a-arc4random_uniform.$(OBJEXT) lib_a-cxa_atexit.$(OBJEXT) \
 	lib_a-cxa_finalize.$(OBJEXT) lib_a-drand48.$(OBJEXT) \
 	lib_a-ecvtbuf.$(OBJEXT) lib_a-efgcvt.$(OBJEXT) \
@@ -128,7 +133,7 @@  am__objects_3 = lib_a-arc4random.$(OBJEXT) \
 	lib_a-wcstoll_r.$(OBJEXT) lib_a-wcstoull.$(OBJEXT) \
 	lib_a-wcstoull_r.$(OBJEXT) lib_a-atoll.$(OBJEXT) \
 	lib_a-llabs.$(OBJEXT) lib_a-lldiv.$(OBJEXT)
-am__objects_4 = lib_a-a64l.$(OBJEXT) lib_a-btowc.$(OBJEXT) \
+am__objects_5 = lib_a-a64l.$(OBJEXT) lib_a-btowc.$(OBJEXT) \
 	lib_a-getopt.$(OBJEXT) lib_a-getsubopt.$(OBJEXT) \
 	lib_a-l64a.$(OBJEXT) lib_a-malign.$(OBJEXT) \
 	lib_a-mbrlen.$(OBJEXT) lib_a-mbrtowc.$(OBJEXT) \
@@ -137,22 +142,23 @@  am__objects_4 = lib_a-a64l.$(OBJEXT) lib_a-btowc.$(OBJEXT) \
 	lib_a-valloc.$(OBJEXT) lib_a-wcrtomb.$(OBJEXT) \
 	lib_a-wcsnrtombs.$(OBJEXT) lib_a-wcsrtombs.$(OBJEXT) \
 	lib_a-wctob.$(OBJEXT)
-am__objects_5 = lib_a-putenv.$(OBJEXT) lib_a-putenv_r.$(OBJEXT) \
+am__objects_6 = lib_a-putenv.$(OBJEXT) lib_a-putenv_r.$(OBJEXT) \
 	lib_a-setenv.$(OBJEXT) lib_a-setenv_r.$(OBJEXT)
-am__objects_6 = lib_a-rpmatch.$(OBJEXT) lib_a-system.$(OBJEXT)
-@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@am__objects_7 = $(am__objects_4) \
-@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@	$(am__objects_5) \
-@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@	$(am__objects_6)
-@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_TRUE@am__objects_7 = $(am__objects_4) \
-@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_TRUE@	$(am__objects_5)
-@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_TRUE@am__objects_7 =  \
-@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_TRUE@	$(am__objects_4)
-@USE_LIBTOOL_FALSE@am_lib_a_OBJECTS = $(am__objects_2) \
-@USE_LIBTOOL_FALSE@	$(am__objects_3) $(am__objects_7)
+am__objects_7 = lib_a-rpmatch.$(OBJEXT) lib_a-system.$(OBJEXT)
+@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@am__objects_8 = $(am__objects_5) \
+@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@	$(am__objects_6) \
+@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@	$(am__objects_7)
+@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_TRUE@am__objects_8 = $(am__objects_5) \
+@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_TRUE@	$(am__objects_6)
+@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_TRUE@am__objects_8 =  \
+@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_TRUE@	$(am__objects_5)
+@USE_LIBTOOL_FALSE@am_lib_a_OBJECTS = $(am__objects_3) \
+@USE_LIBTOOL_FALSE@	$(am__objects_4) $(am__objects_8)
 lib_a_OBJECTS = $(am_lib_a_OBJECTS)
 LTLIBRARIES = $(noinst_LTLIBRARIES)
-@HAVE_LONG_DOUBLE_TRUE@am__objects_8 = strtold.lo wcstold.lo
-am__objects_9 = __adjust.lo __atexit.lo __call_atexit.lo __exp10.lo \
+@HAVE_LONG_DOUBLE_TRUE@am__objects_9 = strtold.lo wcstold.lo
+@NEWLIB_RETARGETABLE_LOCKING_TRUE@am__objects_10 = lock.lo
+am__objects_11 = __adjust.lo __atexit.lo __call_atexit.lo __exp10.lo \
 	__ten_mu.lo _Exit.lo abort.lo abs.lo aligned_alloc.lo \
 	assert.lo atexit.lo atof.lo atoff.lo atoi.lo atol.lo calloc.lo \
 	div.lo dtoa.lo dtoastub.lo environ.lo envlock.lo eprintf.lo \
@@ -163,28 +169,28 @@  am__objects_9 = __adjust.lo __atexit.lo __call_atexit.lo __exp10.lo \
 	rand_r.lo random.lo realloc.lo reallocf.lo sb_charsets.lo \
 	strtod.lo strtodg.lo strtol.lo strtorx.lo strtoul.lo utoa.lo \
 	wcstod.lo wcstol.lo wcstoul.lo wcstombs.lo wcstombs_r.lo \
-	wctomb.lo wctomb_r.lo $(am__objects_8)
-am__objects_10 = arc4random.lo arc4random_uniform.lo cxa_atexit.lo \
+	wctomb.lo wctomb_r.lo $(am__objects_9) $(am__objects_10)
+am__objects_12 = arc4random.lo arc4random_uniform.lo cxa_atexit.lo \
 	cxa_finalize.lo drand48.lo ecvtbuf.lo efgcvt.lo erand48.lo \
 	jrand48.lo lcong48.lo lrand48.lo mrand48.lo msize.lo mtrim.lo \
 	nrand48.lo rand48.lo seed48.lo srand48.lo strtoll.lo \
 	strtoll_r.lo strtoull.lo strtoull_r.lo wcstoll.lo wcstoll_r.lo \
 	wcstoull.lo wcstoull_r.lo atoll.lo llabs.lo lldiv.lo
-am__objects_11 = a64l.lo btowc.lo getopt.lo getsubopt.lo l64a.lo \
+am__objects_13 = a64l.lo btowc.lo getopt.lo getsubopt.lo l64a.lo \
 	malign.lo mbrlen.lo mbrtowc.lo mbsinit.lo mbsnrtowcs.lo \
 	mbsrtowcs.lo on_exit.lo valloc.lo wcrtomb.lo wcsnrtombs.lo \
 	wcsrtombs.lo wctob.lo
-am__objects_12 = putenv.lo putenv_r.lo setenv.lo setenv_r.lo
-am__objects_13 = rpmatch.lo system.lo
-@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@am__objects_14 = $(am__objects_11) \
-@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@	$(am__objects_12) \
-@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@	$(am__objects_13)
-@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_TRUE@am__objects_14 = $(am__objects_11) \
-@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_TRUE@	$(am__objects_12)
-@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_TRUE@am__objects_14 =  \
-@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_TRUE@	$(am__objects_11)
-@USE_LIBTOOL_TRUE@am_libstdlib_la_OBJECTS = $(am__objects_9) \
-@USE_LIBTOOL_TRUE@	$(am__objects_10) $(am__objects_14)
+am__objects_14 = putenv.lo putenv_r.lo setenv.lo setenv_r.lo
+am__objects_15 = rpmatch.lo system.lo
+@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@am__objects_16 = $(am__objects_13) \
+@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@	$(am__objects_14) \
+@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_FALSE@	$(am__objects_15)
+@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_TRUE@am__objects_16 = $(am__objects_13) \
+@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_FALSE@@ELIX_LEVEL_3_TRUE@	$(am__objects_14)
+@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_TRUE@am__objects_16 =  \
+@ELIX_LEVEL_1_FALSE@@ELIX_LEVEL_2_TRUE@	$(am__objects_13)
+@USE_LIBTOOL_TRUE@am_libstdlib_la_OBJECTS = $(am__objects_11) \
+@USE_LIBTOOL_TRUE@	$(am__objects_12) $(am__objects_16)
 libstdlib_la_OBJECTS = $(am_libstdlib_la_OBJECTS)
 libstdlib_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
 	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
@@ -367,7 +373,7 @@  GENERAL_SOURCES = __adjust.c __atexit.c __call_atexit.c __exp10.c \
 	quick_exit.c rand.c rand_r.c random.c realloc.c reallocf.c \
 	sb_charsets.c strtod.c strtodg.c strtol.c strtorx.c strtoul.c \
 	utoa.c wcstod.c wcstol.c wcstoul.c wcstombs.c wcstombs_r.c \
-	wctomb.c wctomb_r.c $(am__append_1)
+	wctomb.c wctomb_r.c $(am__append_1) $(am__append_2)
 @NEWLIB_NANO_MALLOC_FALSE@MALIGNR = malignr
 @NEWLIB_NANO_MALLOC_TRUE@MALIGNR = nano-malignr
 @NEWLIB_NANO_MALLOC_FALSE@MALLOPTR = malloptr
@@ -1002,6 +1008,12 @@  lib_a-wcstold.o: wcstold.c
 lib_a-wcstold.obj: wcstold.c
 	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-wcstold.obj `if test -f 'wcstold.c'; then $(CYGPATH_W) 'wcstold.c'; else $(CYGPATH_W) '$(srcdir)/wcstold.c'; fi`
 
+lib_a-lock.o: lock.c
+	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-lock.o `test -f 'lock.c' || echo '$(srcdir)/'`lock.c
+
+lib_a-lock.obj: lock.c
+	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-lock.obj `if test -f 'lock.c'; then $(CYGPATH_W) 'lock.c'; else $(CYGPATH_W) '$(srcdir)/lock.c'; fi`
+
 lib_a-arc4random.o: arc4random.c
 	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(lib_a_CFLAGS) $(CFLAGS) -c -o lib_a-arc4random.o `test -f 'arc4random.c' || echo '$(srcdir)/'`arc4random.c
 
diff --git a/newlib/libc/stdlib/lock.c b/newlib/libc/stdlib/lock.c
new file mode 100644
index 0000000000000000000000000000000000000000..22366b01917e5dd5d79daaa65a6b2ae9ade98e7d
--- /dev/null
+++ b/newlib/libc/stdlib/lock.c
@@ -0,0 +1,73 @@ 
+/*
+FUNCTION
+<<__lock_init>>, <<__lock_close>>, <<__lock_acquire>>, <<__lock_try_acquire>>, <<__lock_release>>---locking routines
+
+INDEX
+	__lock_init
+INDEX
+	__lock_close
+INDEX
+	__lock_acquire
+INDEX
+	__lock_try_acquire
+INDEX
+	__lock_release
+
+ANSI_SYNOPSIS
+	#include <lock.h>
+	void __lock_init (_LOCK_T <[lock]>);
+	void __lock_close (_LOCK_T <[lock]>);
+	void __lock_acquire (_LOCK_T <[lock]>);
+	void __lock_try_acquire (_LOCK_T <[lock]>);
+	void __lock_release (_LOCK_T <[lock]>);
+
+TRAD_SYNOPSIS
+	void __lock_init(<[lock]>)
+	_LOCK_T <[lock]>;
+
+	void __lock_close(<[lock]>)
+	_LOCK_T <[lock]>;
+
+	void __lock_acquire(<[lock]>)
+	_LOCK_T <[lock]>;
+
+	void __lock_try_acquire(<[lock]>)
+	_LOCK_T <[lock]>;
+
+	void __lock_release(<[lock]>)
+	_LOCK_T <[lock]>;
+
+DESCRIPTION
+These empty functions are used for locking to allow applications to retarget
+them.  They are used in place of the default dummy lock macros when newlib
+is configured with --enable-newlib-retargetable-locking.
+*/
+
+/* dummy lock routines for single-threaded apps */
+
+#include <sys/lock.h>
+
+void _ATTRIBUTE((__weak__))
+__lock_init (_LOCK_T lock)
+{
+}
+
+void _ATTRIBUTE((__weak__))
+__lock_close(_LOCK_T lock)
+{
+}
+
+void _ATTRIBUTE((__weak__))
+__lock_acquire (_LOCK_T lock)
+{
+}
+
+void _ATTRIBUTE((__weak__))
+__lock_try_acquire(_LOCK_T lock)
+{
+}
+
+void _ATTRIBUTE((__weak__))
+__lock_release (_LOCK_T lock)
+{
+}
diff --git a/newlib/newlib.hin b/newlib/newlib.hin
index d03dfac0eea6a8917a92b8f0f3584c2cd42b9388..397bc9b96eafddace3a75be509157040654b6fde 100644
--- a/newlib/newlib.hin
+++ b/newlib/newlib.hin
@@ -82,6 +82,9 @@ 
 /* Define if small footprint nano-formatted-IO implementation used.  */
 #undef _NANO_FORMATTED_IO
 
+/* Define if using retargetable functions for default lock routines.  */
+#undef _RETARGETABLE_LOCKING
+
 /*
  * Iconv encodings enabled ("to" direction)
  */