1

This is code from MSDN (using singly linked list):

typedef struct _PROGRAM_ITEM 
 {
    SLIST_ENTRY ItemEntry;
    ULONG Signature; 

    /* MY DATA */

 } PROGRAM_ITEM, *PPROGRAM_ITEM;

int main( )
{
    ULONG Count;
    PSLIST_ENTRY pFirstEntry, pListEntry;
    PSLIST_HEADER pListHead;
    PPROGRAM_ITEM pProgramItem;
    pListHead = (PSLIST_HEADER)_aligned_malloc(sizeof(SLIST_HEADER),
    MEMORY_ALLOCATION_ALIGNMENT);

    InitializeSListHead(pListHead);

   // Insert 10 items into the list.
   for( Count = 1; Count <= 10; Count += 1 )
   {
     pProgramItem = (PPROGRAM_ITEM)_aligned_malloc(sizeof(PROGRAM_ITEM),
        MEMORY_ALLOCATION_ALIGNMENT);
     pProgramItem->Signature = Count;
     pFirstEntry = InterlockedPushEntrySList(pListHead, 
                   &(pProgramItem->ItemEntry)); 
   }

   // Remove 10 items from the list and display the signature.
  for( Count = 10; Count >= 1; Count -= 1 )
   {
     pListEntry = InterlockedPopEntrySList(pListHead);

     pProgramItem = (PPROGRAM_ITEM)pListEntry;
     printf("Signature is %d\n", pProgramItem->Signature);

    _aligned_free(pListEntry);
   }

  pListEntry = InterlockedFlushSList(pListHead);
  pFirstEntry = InterlockedPopEntrySList(pListHead);
  if (pFirstEntry != NULL)
  {
    printf("Error: List is not empty.\n");
    return -1;
  }

  _aligned_free(pListHead);

  return 1;
}

It works well under one condition: SLIST_ENTRY must be first member in _PROGRAM_ITEM structure. But what if my _PROGRAM_ITEM should look like:

typedef struct _PROGRAM_ITEM 
{
  /* MY DATA */

  SLIST_ENTRY ItemEntry;
  ULONG Signature; 

} PROGRAM_ITEM, *PPROGRAM_ITEM;

In this case InterlockedPushEntrySList,InterlockedPopEntrySList etc. dont't work properly. HOW to deal with this situation?

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
maciekm
  • 257
  • 1
  • 5
  • 28

2 Answers2

3

It has to be that way because InterlockedPushEntrySList and family take a PSLIST_ENTRY. If ItemEntry is the first item in the list, you effectively have (void*)pProgramItem == (void*)&pProgramItem->ItemEntry. This also means that when you pop the entry off the list, you need only cast the PSLIST_ENTRY back to a PPROGRAM_ITEM.

Having it any other way is difficult, because an SLIST_ENTRY is required to be aligned as per MEMORY_ALLOCATION_ALIGNMENT. You would need any data in your _PROGRAM_ITEM struct that comes before the SLIST_ENTRY to be a multiple of this (or to pad out to this). Not to mention you need to do some pointer arithmetic to get your PPROGRAM_ITEM back from the PSLIST_ENTRY when you pop it off the list.

Is there any specific reason why you'd want your data to come first? It's much simpler to have the SLIST_ENTRY as the first data member.

Anthony
  • 12,177
  • 9
  • 69
  • 105
  • the reason is that i want to store in this list WSAOVERLAPPED extended structure, where first place is already busy for overlapped structure... – maciekm Jan 05 '14 at 13:28
  • Store a pointer to the `WSAOVERLAPPED` structure in your `PROGRAM_ITEM` and dereference. No need to try to make both work in the same piece of memory. – Anthony Jan 05 '14 at 13:34
  • thanks for answer, can you look to this post http://stackoverflow.com/questions/20937334/casting-issue-with-linked-list-item which refers to the above? – maciekm Jan 05 '14 at 18:26
1

You can use the CONTAINING_RECORD macro to get a pointer to your data.

pProgramItem = (PPROGRAM_ITEM)CONTAINING_RECORD(pListEntry, PROGRAM_ITEM, ItemEntry);

Although the InterlockedPushEntrySList has an alignment requirement for its parameters. You could use a static_assert to ensure ItemEntry is aligned properly:

static_assert((FIELD_OFFSET(PROGRAM_ITEM, ItemEntry) % \
    MEMORY_ALLOCATION_ALIGNMENT) == 0, "Alignment check.");
Shaun
  • 341
  • 3
  • 7
Jason
  • 921
  • 9
  • 15