Unsafe(Mutable)Pointer (suc)predecessor and advancedBy functions


(Adrian Zubarev) #1

I’ve got one more questions about Unsafe(Mutable)Pointer. I know that I’m able to access memory that might not belong to me.

My question is:

Can I trust these functions that they will return a pointer to some memory when I allocate more than one object AND when I’m moving only inside that range?

public func successor() -> UnsafeMutablePointer<Memory>
public func predecessor() -> UnsafeMutablePointer<Memory>
public func advancedBy(n: Int) -> UnsafeMutablePointer<Memory>
UnsafeMutablePointer<Int>.alloc(4) when I advance only in range of [0,1,2,3] am I safe or could I get a pointer to memory that does not belong to me?

Example:

// imagine this is some memory portion,
// where x is memory that does not belong to me
// and 0 is moemory free to use
     
[…, x, 0, 0, 0 x, 0, x, …]
     
// now I want to allocate 4 objects
// variant A:
     
[…, x, MY1, MY2, MY3, x, MY4, x, …]
     
// my pointer will sit at `MY1` and if I advance by 2 I'll get `x`
// can this happen to me?
     
// variant B:
// Unsafe(Mutable)Pointer will ensure that I always get memory tied together
// (or the above functions will skip memory that doesn't belong to me??):
     
[…, x, MY1, MY2, MY3, MY4 x, …]
So which is right?

···

--
Adrian Zubarev
Sent with Airmail


(Andrew Trick) #2

I’ve got one more questions about Unsafe(Mutable)Pointer. I know that I’m able to access memory that might not belong to me.

My question is:

Can I trust these functions that they will return a pointer to some memory when I allocate more than one object AND when I’m moving only inside that range?

Yes.

public func successor() -> UnsafeMutablePointer<Memory>
public func predecessor() -> UnsafeMutablePointer<Memory>
public func advancedBy(n: Int) -> UnsafeMutablePointer<Memory>
UnsafeMutablePointer<Int>.alloc(4) when I advance only in range of [0,1,2,3] am I safe or could I get a pointer to memory that does not belong to me?

UnsafeMutablePointer<T>.alloc(N) creates a single object in memory that holds N consecutive T values. Each value resides at index*strideof(T.self) bytes beyond the allocated pointer where index is valid in the range 0..<N.

-Andy

···

On May 26, 2016, at 9:59 AM, Adrian Zubarev via swift-users <swift-users@swift.org> wrote:
Example:

// imagine this is some memory portion,
// where x is memory that does not belong to me
// and 0 is moemory free to use
     
[…, x, 0, 0, 0 x, 0, x, …]
     
// now I want to allocate 4 objects
// variant A:
     
[…, x, MY1, MY2, MY3, x, MY4, x, …]
     
// my pointer will sit at `MY1` and if I advance by 2 I'll get `x`
// can this happen to me?
     
// variant B:
// Unsafe(Mutable)Pointer will ensure that I always get memory tied together
// (or the above functions will skip memory that doesn't belong to me??):
     
[…, x, MY1, MY2, MY3, MY4 x, …]
So which is right?

--
Adrian Zubarev
Sent with Airmail

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users


(Adrian Zubarev) #3

So theoretically I could build a wrapper type for Unsafe(Mutable)Pointer which will be safe to use and never exceed the allocated range!

public func successor() -> UnsafeMutablePointer<Memory>? {
     
    // return `nil` if out of range
}
So why don’t we have safe pointers today?
Any technical reasons or do I miss something here?!

···

--
Adrian Zubarev
Sent with Airmail

Am 26. Mai 2016 bei 19:14:41, Andrew Trick (atrick@apple.com) schrieb:

On May 26, 2016, at 9:59 AM, Adrian Zubarev via swift-users <swift-users@swift.org> wrote:

I’ve got one more questions about Unsafe(Mutable)Pointer. I know that I’m able to access memory that might not belong to me.

My question is:

Can I trust these functions that they will return a pointer to some memory when I allocate more than one object AND when I’m moving only inside that range?

Yes.
public func successor() -> UnsafeMutablePointer<Memory>
public func predecessor() -> UnsafeMutablePointer<Memory>
public func advancedBy(n: Int) -> UnsafeMutablePointer<Memory>

UnsafeMutablePointer<Int>.alloc(4) when I advance only in range of [0,1,2,3] am I safe or could I get a pointer to memory that does not belong to me?

UnsafeMutablePointer<T>.alloc(N) creates a single object in memory that holds N consecutive T values. Each value resides at index*strideof(T.self) bytes beyond the allocated pointer where index is valid in the range 0..<N.

-Andy
Example:

// imagine this is some memory portion,
// where x is memory that does not belong to me
// and 0 is moemory free to use
      
[…, x, 0, 0, 0 x, 0, x, …]
      
// now I want to allocate 4 objects
// variant A:
      
[…, x, MY1, MY2, MY3, x, MY4, x, …]
      
// my pointer will sit at `MY1` and if I advance by 2 I'll get `x`
// can this happen to me?
      
// variant B:
// Unsafe(Mutable)Pointer will ensure that I always get memory tied together
// (or the above functions will skip memory that doesn't belong to me??):
      
[…, x, MY1, MY2, MY3, MY4 x, …]

So which is right?

--
Adrian Zubarev
Sent with Airmail

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Austin Zheng) #4

So theoretically I could build a wrapper type for Unsafe(Mutable)Pointer
which will be safe to use and never exceed the allocated range!

Yeah, if I remember correctly this is actually how the Swift collections
are implemented.

public func successor() -> UnsafeMutablePointer<Memory>? {

    // return `nil` if out of range
}

   1. So why don’t we have safe pointers today?
   2. Any technical reasons or do I miss something here?!

The check for safety imposes a performance cost, which may or may not be

okay.

It's also not clear sometimes exactly what "out of bounds" means - for
example, you might have a big chunk of memory representing an array, and
then you take a pointer to only part of that memory, representing a slice
of the array. In this case you can write "out of bounds" of the slice, but
the pointer type doesn't know that (because you are still within the range
of the chunk of memory that you got from `UnsafeMutablePointer.memory()`).

The way Swift has you do it is that you can do whatever you want with
pointers, but it's up to you to decide exactly what "in bounds" and "out of
bounds" means, and then wrap that all up in a wrapper type with the
appropriate checks. That way you can have the raw performance if you really
need it, and you can have safety otherwise.

···

On Thu, May 26, 2016 at 10:31 AM, Adrian Zubarev via swift-users < swift-users@swift.org> wrote:

--
Adrian Zubarev
Sent with Airmail

Am 26. Mai 2016 bei 19:14:41, Andrew Trick (atrick@apple.com) schrieb:

On May 26, 2016, at 9:59 AM, Adrian Zubarev via swift-users < > swift-users@swift.org> wrote:

I’ve got one more questions about Unsafe(Mutable)Pointer. I know that I’m
able to access memory that might not belong to me.

My question is:

   -

   Can I trust these functions that they will return a pointer to some
   memory when I allocate more than one object AND when I’m moving only inside
   that range?

Yes.

   -

   public func successor() -> UnsafeMutablePointer<Memory>
   public func predecessor() -> UnsafeMutablePointer<Memory>
   public func advancedBy(n: Int) -> UnsafeMutablePointer<Memory>

   -

   UnsafeMutablePointer<Int>.alloc(4) when I advance only in range of
   [0,1,2,3] am I safe or could I get a pointer to memory that does not
   belong to me?

UnsafeMutablePointer<T>.alloc(N) creates a single object in memory that
holds N consecutive T values. Each value resides at index*strideof(T.self)
bytes beyond the allocated pointer where index is valid in the range 0..<N.

-Andy

   -

   Example:

   // imagine this is some memory portion,
   // where x is memory that does not belong to me
   // and 0 is moemory free to use

   […, x, 0, 0, 0 x, 0, x, …]

   // now I want to allocate 4 objects
   // variant A:

   […, x, MY1, MY2, MY3, x, MY4, x, …]

   // my pointer will sit at `MY1` and if I advance by 2 I'll get `x`
   // can this happen to me?

   // variant B:
   // Unsafe(Mutable)Pointer will ensure that I always get memory tied together
   // (or the above functions will skip memory that doesn't belong to me??):

   […, x, MY1, MY2, MY3, MY4 x, …]

So which is right?

--
Adrian Zubarev
Sent with Airmail

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Adrian Zubarev) #5

It's also not clear sometimes exactly what "out of bounds" means - for example, you might have a big chunk of memory representing an array, and then you take a pointer to only part of that memory, representing a slice of the array. In this case you can write "out of bounds" of the slice, but the pointer type doesn't know that (because you are still within the range of the chunk of memory that you got from `UnsafeMutablePointer.memory()`).

True story. :smiley:
Thank you for clarifying that to me, its a good example. Also the new pointer that I’ll get here won’t be a slice of an array just because `Memory` isn’t a slice. I’ll have to cast the pointer first, but I got the point here. :wink:

One more thing:

- How does ARC work here when I create a new pointer to one of my allocated objects?
- Do I have 2 strong references to my main piece of memory?

···

--
Adrian Zubarev
Sent with Airmail

Am 26. Mai 2016 bei 20:07:36, Austin Zheng (austinzheng@gmail.com) schrieb:


(Austin Zheng) #6

This is where it gets tricky.

When you create a chunk of memory using 'UnsafeMutablePointer.memory()' or
'alloc()', it's as if you are programming in C with 'malloc' and 'free'.
The memory you create and the objects you put in that memory don't
participate in ARC - the runtime will not track reference counts or
automatically free the memory. The memory will live on forever unless you
explicitly call 'dealloc()' later.

Using these APIs correctly is quite hard. If you're not careful you can
leak memory (if you lose all pointers before you've had a chance to call
dealloc), or access invalid memory (you called dealloc earlier, but
somewhere else you later access that memory). In a lot of cases a good
thing to do is to wrap your unsafe memory buffer inside a regular Swift
class, only allow the buffer to be accessed or modified through that class,
and have that class be responsible for deallocating the buffer when its
deinit is called. This way, you tie the lifetime of that buffer to your
Swift class and ARC handles the memory management for you.

Best,
Austin

···

On Thu, May 26, 2016 at 11:19 AM, Adrian Zubarev via swift-users < swift-users@swift.org> wrote:

It's also not clear sometimes exactly what "out of bounds" means - for
example, you might have a big chunk of memory representing an array, and
then you take a pointer to only part of that memory, representing a slice
of the array. In this case you can write "out of bounds" of the slice, but
the pointer type doesn't know that (because you are still within the range
of the chunk of memory that you got from `UnsafeMutablePointer.memory()`).

True story. :smiley:
Thank you for clarifying that to me, its a good example. Also the new
pointer that I’ll get here won’t be a slice of an array just because
`Memory` isn’t a slice. I’ll have to cast the pointer first, but I got the
point here. :wink:

One more thing:

- How does ARC work here when I create a new pointer to one of my
allocated objects?
- Do I have 2 strong references to my main piece of memory?

--
Adrian Zubarev
Sent with Airmail

Am 26. Mai 2016 bei 20:07:36, Austin Zheng (austinzheng@gmail.com)
schrieb:

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Adrian Zubarev) #7

This is where it gets tricky.

When you create a chunk of memory using 'UnsafeMutablePointer.memory()' or 'alloc()', it's as if you are programming in C with 'malloc' and 'free'. The memory you create and the objects you put in that memory don't participate in ARC - the runtime will not track reference counts or automatically free the memory. The memory will live on forever unless you explicitly call 'dealloc()' later.
Check.

Using these APIs correctly is quite hard. If you're not careful you can leak memory (if you lose all pointers before you've had a chance to call dealloc), or access invalid memory (you called dealloc earlier, but somewhere else you later access that memory). In a lot of cases a good thing to do is to wrap your unsafe memory buffer inside a regular Swift class, only allow the buffer to be accessed or modified through that class, and have that class be responsible for deallocating the buffer when its deinit is called. This way, you tie the lifetime of that buffer to your Swift class and ARC handles the memory management for you.
Exactly what I’m doing right now. :slight_smile: But I’m still a little afraid when using these types.

···

--
Adrian Zubarev
Sent with Airmail