News for hackers. Stuff that matters.
Re:Baby's First Garbage Collector by phamilton on Sunday December 08, 2013 @04:51PM (#6871585)
Not having studied the topic in depth, my first thought is whether true pointers and a garbage collector can coexist (without artificially removing parts of the language). The big condition for knowing if something is still in use is whether any references exist to it. But in a language like C , you could cast a pointer to something else (like a void pointer for some quasi generic linked list), store it somewhere, and then cast it back later after garbage collection has run. That seems like a problematic situation. So is completely giving up on pointers (or just never doing tricky things like casting or pointer arithmetic) a requirement to having a garbage collector?
Re:Baby's First Garbage Collector by bunderbunder on Sunday December 08, 2013 @08:06PM (#6872343)
C# has pointer goodness, in that you can do pointer arithmetic and bypass type safety.

However, you have to declare an 'unsafe' context in which you'll do it. Unsafe contexts are tied to a local scope, which means that you can't do anything like having classes with fields of pointer types. Pointers don't outlive the function in which you use them.

Also, before you can get a pointer to an object you have to "pin" it, which tells the garbage collector not to move it around. This is necessary, of course, but it can have severe performance implications since it basically torpedoes all the garbage collector's best performance optimizations.

So pointers are there when you need them. . . but the language makes them extremely inconvenient to use them more than you absolutely need to. A pretty solid compromise, IMO.

Re:Baby's First Garbage Collector by MichaelGG on Sunday December 08, 2013 @08:31PM (#6872423)
C# can also use managed pointers (type& instead of type*). All out/ref parameters use this type of pointer, and it'll get updated by the runtime, like an object reference - no pinning necessary.
Re:Baby's First Garbage Collector by kevingadd on Sunday December 08, 2013 @10:24PM (#6872700)
Interesting, can you link to an example of their use? I've never seen them in the documentation or examples, but I remember that Managed C++ and C++/CLI have something similar.

Or are you just saying that you can 'use' them via out/ref? I interpreted your post to mean that you can store them in fields, which would be stellar but seems like it would break reference lifetime guarantees.

Re:Baby's First Garbage Collector by MichaelGG on Monday December 09, 2013 @01:09PM (#6875665)
You only use them via out/ref. Section 13.4.2 of the CLI spec says:

- Managed pointers can be passed as arguments, stored in local variables, and returned as values.

- Managed pointers cannot be stored in static variables, array elements, or fields of objects or value types.

So you're right, they have very limited scope as it'd be messy otherwise. It's my understanding that the JVM does not have pointers even at this level, hence Java can't do out/ref parameters. (Sure, tuples are a better way to handle many cases of out params, but sometimes a well chosen ref can really be nicer.)

I think C++ also uses the term "managed pointer" but not in the same technical sense (I think it just means reference.) Using "safe" C++/CLI generation limits the scope of pointers just like we'd expect.

Re:Baby's First Garbage Collector by kevingadd on Sunday December 08, 2013 @10:22PM (#6872696)
You can't store a raw pointer, but you can store the 'safe' forms of pointers (IntPtr, etc) which allows you to do some of the stuff you'd want to do - allocate a buffer of given size in unmanaged heap, then manipulate it entirely using struct pointers and pointer arithmetic. The key/value store I wrote in C# does this - no data lives in the managed heap, it's all sitting in unmanaged memory (mapped files, as it happens).
Re:Baby's First Garbage Collector by MaulingMonkey on Sunday December 08, 2013 @10:35PM (#6872718)
> You can't store a raw pointer

You can if you mark the containing class/struct "unsafe", which allows both type safety and increased ease of use.

Re:Baby's First Garbage Collector by kevingadd on Sunday December 08, 2013 @11:29PM (#6872816)
Hmmm, I've never seen this done. Thanks for the heads-up, I'll have to try it out. Are you sure you can do it with classes? I would not be shocked by being able to do it with structs.
Re:Baby's First Garbage Collector by MaulingMonkey on Sunday December 08, 2013 @11:36PM (#6872832)
Sure am.

  using System;
  
  namespace Test2012
  {
  	unsafe class C  { public byte* P; public override string ToString( ) { return new IntPtr(P).ToString( ); } }
  	unsafe struct S { public byte* P; public override string ToString( ) { return new IntPtr(P).ToString( ); } }
  
  	static class Program
  	{
  		static unsafe void Main( )
  		{
  			fixed( byte* test = new byte[] { 1, 2, 3 } )
  			{
  				var c = new C( ) { P = test };
  				var s = new S( ) { P = test };
  				Console.WriteLine( "Test: {0}", c );
  				Console.WriteLine( "Test: {0}", s );
  			}
  		}
  	}
  }
Result:

  Test: 40510364
  Test: 40510364
  Press any key to continue . . .
Re:Baby's First Garbage Collector by rayiner on Sunday December 08, 2013 @05:11PM (#6871670)
You don't need to give up pointers, or pointer arithmetic, just the ability to obscure what a pointer points to. Casting to void does not do this (and indeed, has no run time effect at all). Metadata is associated with the pointed-to block of memory, so as long as the address is recognizable as a pointer at runtime, you can do GC.

Some of the things that break GC aren't even technically valid C code. For example, say I allocate three memory areas with malloc() then hold on only to the area with the lowest address, and reference the other two areas with offsets relative to the first area. This is illegal in C, though it will work on most existing implementations, because it's illegal to dereference a pointer past the end of an allocation.

Specific GC algorithms might not be able to handle say pointers into the interiors of objects, but that's not a general limitation for GC.

Re:Baby's First Garbage Collector by cperciva on Sunday December 08, 2013 @05:20PM (#6871707)
it's illegal to dereference a pointer past the end of an allocation.

Yes, but there's a legal way to do this.

reference the other two areas with offsets relative to the first area

If you cast the pointers to uintptr_t, and perform your arithmetic on uintptr_t and cast your final pointer back (void * ) before using it, what you've done is perfectly legal and safe (albeit weird) since uintptr_t is an unsigned integer type with all the flexibility of unsigned integers, and if x = (uintptr_t)p then it's guaranteed that p == (void * )x.

In other words:

    char * x = malloc(100);
    char * y = malloc(100);
    ptrdiff_t yminusx = y - x;

    x[yminusx] = '\0'
is undefined behaviour, but

    char * x = malloc(100);
    char * y = malloc(100);
    uintptr_t yminusx = (uintptr_t)(y) - (uintptr_t)(x);

    *(char *)(void *)((uintptr_t)(x) + yminusx) = '\0'
is valid C (and has the same meaning on a DWIM compiler).
Re:Baby's First Garbage Collector by bcoates on Sunday December 08, 2013 @05:41PM (#6871797)
It looks like the second is still valid C, but a C++11 compiler is allowed to break it if it declares "strict pointer safety" rules are in effect at runtime (which a GC implementation would presumably do)
Re:Baby's First Garbage Collector by logancapaldo on Sunday December 08, 2013 @05:49PM (#6871828)
What if it's not a DWIM compiler? I'm pretty sure (happy to be corrected) pointer <-> integer conversions are implementation defined. Therefore casting between could perform some reversible operation that ensured round tripping worked, but does not require that ptr(a - b) == a - b even though a == int(ptr(a)) holds.
Re:Baby's First Garbage Collector by cperciva on Sunday December 08, 2013 @07:23PM (#6872220)
I'm pretty sure (happy to be corrected) pointer <-> integer conversions are implementation defined.

Quite correct, subject to the requirement that converting the same uintptr_t value back to (void ) will return the original pointer. So the conversion could be implemented as a hash table if the compiler author wanted.

But if you look carefully at what I wrote, I'm never converting a value which was not previously returned by a (void )->(uintptr_t) conversion.

Re:Baby's First Garbage Collector by logancapaldo on Sunday December 08, 2013 @08:22PM (#6872391)
I see. What I was worried about was a version of the code you didn't actually write (casting the ptrdiff_t result to a [u]intptr_t, not casting the pointers first).
Re:Baby's First Garbage Collector by rayiner on Sunday December 08, 2013 @05:36PM (#6871779)
Right. You could also convert a pointer to a uintptr_t, XOR it with some value, then at some later point XOR it again to get back the original address and cast it back to a pointer and dereference it, and it would be legal despite breaking GC. However, a number of things that also break GC, like your first example, also happen not to be legal C even though they'll probably work on any existing implementation.
Re:Baby's First Garbage Collector by Someone on Sunday December 08, 2013 @06:38PM (#6872030)
That's very close to the classical example of code that maims pointers: the doubly linked list that uses only one 'pointer' in each node (http://www.geeksforgeeks.org/xor-linked-list-a-memory-effici...)
Re:Baby's First Garbage Collector by city41 on Sunday December 08, 2013 @05:16PM (#6871689)
Does this still work if the GC compacts memory? I don't know much about GCs, but it seems like once your GC is advanced enough with generational collecting, compacting, etc, then allowing raw pointers seems at odds with what the GC is trying todo. I'm sure it's possible, but it's probably a heck of a lot easier just to not allow pointers.
Re:Baby's First Garbage Collector by rayiner on Sunday December 08, 2013 @05:39PM (#6871789)
It works fine as long as you have enough type information to know what is and is not a pointer.
Re:Baby's First Garbage Collector by jballanc on Sunday December 08, 2013 @05:06PM (#6871643)
When you hear talk of "conservative" Garbage Collectors, what the "conservative" refers to is that anything on the stack that looks like a pointer is treated as if it is a pointer. After all, it doesn't matter what you cast a pointer to, it's all just 1s and 0s on the stack...
Re:Baby's First Garbage Collector by dllthomas on Sunday December 08, 2013 @07:07PM (#6872159)
Of course, you can make something look like not a pointer and then make it a pointer again if you actually change the zeros and ones.
Re:Baby's First Garbage Collector by _delirium on Sunday December 08, 2013 @07:40PM (#6872274)
Most languages don't give you any guarantees if you do that, though. In C, for example, pointer arithmetic on a void*, or on a pointer cast to an integer, is not guaranteed to produce sensible results. The standard only provides guarantees for pointer arithmetic if it's performed on a non-void pointer that points within an array, and the results remain within the bounds of the same array (arithmetic that produces a pointer pointing outside the array of the base pointer is undefined, with the exception that pointing to one element past the end of the array is defined).
Re:Baby's First Garbage Collector by dllthomas on Sunday December 08, 2013 @10:19PM (#6872689)
In ordinary C, without a garbage collector, I'd be baffled to find an implementation where one could not:

    1) Cast a pointer to an integer of sufficient size.
    2) Xor that integer with another value.
    3) Xor the result with the same value.
    4) Cast back to a pointer.
    5) Dereference.
It's sufficiently corner-case that I am not certain what the standard says about it. In any event, pointers matter when coding in assembly, where language standards aren't even relevant.
Re:Baby's First Garbage Collector by aidenn0 on Sunday December 08, 2013 @11:10PM (#6872782)
That is actually allowed in the standard; it specifically says that casting a pointer to an integral type of sufficient size is a reversible operation (though it I'm not sure if this is not well defined: (int i[2]; (int )((int)i)+sizeof(int)
Re:Baby's First Garbage Collector by dllthomas on Sunday December 08, 2013 @11:20PM (#6872798)
Could you have a standards-conforming implementation where pointers were big-endian and other integral values little-endian? That would break your example code. I don't know of any architecture that would motivate that, of course...
Re:Baby's First Garbage Collector by fhars on Monday December 09, 2013 @01:53AM (#6873137)
You can serialize it to a hex string, put it in a json request that you send off to some cloud service, and then an hour later parse it back out of the json reply, with nothing left in RAM that looks like that pointer for the time inbetween.
Re:Baby's First Garbage Collector by city41 on Sunday December 08, 2013 @04:55PM (#6871603)
I don't know of any language that has pointers and a GC. Are there any? C# lets you use pointers but only if you tell the GC to not move your object and promise to behave.
Re:Baby's First Garbage Collector by wsc981 on Sunday December 08, 2013 @05:36PM (#6871782)
> "I don't know of any language that has pointers and a GC. Are there any?"

Objective-C used to have a garbage collector, though (at least with regards to iOS) Apple removed this capability with the introduction or ARC. On the following URL is some discussion on the Objective-C garbage collector: http://cocoasamurai.blogspot.nl/2010/12/objective-c-memory-m...

Re:Baby's First Garbage Collector by masklinn on Monday December 09, 2013 @04:33AM (#6873531)
iOS never got the GC, only the desktop version got it, and afaik it was deprecated rather than removed entirely.
Re:Baby's First Garbage Collector by pornel on Monday December 09, 2013 @05:36AM (#6873690)
It's been removed entirely in Xcode 5.1.
Re:Baby's First Garbage Collector by betterunix on Sunday December 08, 2013 @06:49PM (#6872073)
Re:Baby's First Garbage Collector by pjmlp on Monday December 09, 2013 @02:54AM (#6873282)
> I don't know of any language that has pointers and a GC. Are there any? C# lets you use pointers but only if you tell the GC to not move your object and promise to behave

Oberon, Oberon-2 allows you to use no-GC pointers via the SYSTEM package.

Active Oberon additionally adds syntax support for untraced pointers.

Modula-3 has unsafe modules which allow you to use untraced pointers.

D has unsafe pointers in system blocks.

Ada and C++11 have support for optional GC defined on the language standard. However so far, there isn't any compiler vendor that really offers them.

In Java you can do a bit of dirty pointer tricks via the com.sun.unsafe package, but it is Oracle's VM specific. Although there are plans to make it part of the official library after Java 8.

Re:Baby's First Garbage Collector by octo_t on Sunday December 08, 2013 @04:58PM (#6871617)
Optional GC[1] in a language like C++, and Rust has pointers with task-local GC

[1] - http://en.wikipedia.org/wiki/Boehm_GC

Re:Baby's First Garbage Collector by jballanc on Sunday December 08, 2013 @05:07PM (#6871651)
The Boehm–Demers–Weiser garbage collector is a fairly famous implementation of a GC for vanilla C, pointers and all: http://en.wikipedia.org/wiki/Boehm_garbage_collector
Re:Baby's First Garbage Collector by city41 on Sunday December 08, 2013 @05:12PM (#6871671)
Yeah I'm aware of optional GCs. What I meant was languages that ship with a GC as standard and also have pointers. In the case of Boehm, since you choose to bring it in yourself, it's up to you to make sure you don't abuse it.
Re:Baby's First Garbage Collector by jballanc on Sunday December 08, 2013 @05:46PM (#6871815)
Well, pretty much any language with a GC can abuse the GC, regardless of whether it's optional or designed in from the start. It just happens to be a lot harder to abuse a GC than to forget to free/delete something.
Re:Baby's First Garbage Collector by Scaevolus on Sunday December 08, 2013 @05:24PM (#6871721)
You can have a GC without compaction.

Go has a GC and pointers, including internal pointers (e.g. to a field of a struct). Its GC started out fully conservative, then precise for the heap and conservative for the stack, and future versions will be fully precise.

Re:Baby's First Garbage Collector by bitwize on Monday December 09, 2013 @07:23AM (#6873969)
Various Lisp dialects over the years have supported the use of locatives, or pointer-like objects.

In general, objects which can be moved or copied around by the runtime behind the programmer's back cannot have indefinite-extent hardware-address pointers taken directly into them. This includes objects managed by most halfway-decent garbage collectors (which tend to be implemented as stop-and-copy, not mark-and-sweep), but also includes such things as drawing surfaces in SDL and DirectDraw. Many runtimes take the C# approach -- provide a memory locking operation that fixes the object in memory while you do some pointer stuff on it and a dual unlocking operation that releases the object so it can be kicked around by the runtime again.

Re:Baby's First Garbage Collector by cwzwarich on Sunday December 08, 2013 @05:29PM (#6871745)
Some languages in the Wirth family have pointers and GC, e.g. Modula-3. It has a module-level notion of safety, so that modules using potentially unsafe pointer manipulation are marked as unsafe and break runtime safety guarantees. It also doesn't guarantee that all pointers will be roots for GC.
Re:Baby's First Garbage Collector by nicky0 on Sunday December 08, 2013 @05:36PM (#6871781)
Objective-C has pointers and a GC.
Re:Baby's First Garbage Collector by aidenn0 on Sunday December 08, 2013 @11:07PM (#6872780)
See this: http://www.hpl.hp.com/personal/Hans_Boehm/gc/

But also with a very small number of restrictions, you can make C have non-conservative GC.

ANSI C forbids aliasing as any type but a character. Furthermore, unions are to be interpreted as the last type they are accessed as.

The three places you run into issues are:

1) You can treat two structs that begin with the same values interchangeably so long as you only access the common beginning of the structures.

2) You can freely convert pointers to integers and back.

3) Storage returned by malloc() can be used heterogeneously (however, with strict aliasing rules, you can't switch types of the data once you've accessed it)

If you forbid those three things, then you can have a strict gc by adding runtime overhead to accessing data (once malloced data is accessed by a non-character type, you can treat it like that type forever).

However, lots and lots of code violates these rules, (the most famous code that does is probably the fast inverse square-root)[1]

http://betterexplained.com/articles/understanding-quakes-fas...