/** @file

  A brief file description

  @section license License

  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
 */

#include "ts/ink_config.h"
#include <cassert>

#include "ts/ink_atomic.h"
#include "ts/ink_queue.h"

/*
 * This file was added during the debugging of Bug 50475.
 * It was found that a race condition was introduced on the sparc architecture
 * when the ink_queue.c file was moved over to ink_queue.cc. Debugging this
 * problem resulted in the discovery that gcc was spitting out the
 * "ldd" (load double) instruction for loading of the 64 bit field "data"
 * while CC was spitting out two "ld" (load) instructions. The old code
 * was calling item.data = head.data on sparcs and not putting any restriction
 * on the order of loading of the fields.
 *
 * This is a problem on the sparcs because the "pointer" field was being loaded
 * before the "version" field. This can result in a very subtle race condition
 * which subverts the addition of the "version" field.
 *
 * Take this scenario
 * item.ptr = head.ptr
 * (call to ink_freelist_new )
 * next.ptr = *(item.ptr) <---- Error
 * (call to ink_freelist_free )
 * item.version = head.version
 * next.version = item.version ++;
 * cas64(head, item, next)

 * Note, that the cas64 call will be succesful and the next.ptr will probably
 * be a pointer into the vtable entry. The next alloc will result in a write into
 * the vtable area.
 *
 * The fix for this problem is to read the version before reading the pointer
 * on 32 bit architectures (currently everything other than alphas). This is
 * done using the following function. This file only contains one function
 * to make looking at the assembly code simple.
 *
 * If you ever change the compiler or the compiler options to this file, make
 * sure you look at the assembly generated to see if the version is read first.
 * Also, make sure that you run the test_freelist microbenchmark for at least
 * 24 hours on a dual processor box.
 */

void
ink_queue_load_64(void *dst, void *src)
{
#if (defined(__i386__) || defined(__arm__) || defined(__mips__)) && (SIZEOF_VOIDP == 4)
  volatile int32_t src_version = (*(head_p *)src).s.version;
  void *src_pointer            = (*(head_p *)src).s.pointer;

  (*(head_p *)dst).s.version = src_version;
  (*(head_p *)dst).s.pointer = src_pointer;
#else
  *(void **)dst = *(void **)src;
#endif
}
