//===--- TaskOptions.h - ABI structures for task options --------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Swift ABI describing task options.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_ABI_TASK_OPTIONS_H
#define SWIFT_ABI_TASK_OPTIONS_H

#include "swift/ABI/Executor.h"
#include "swift/ABI/HeapObject.h"
#include "swift/ABI/Metadata.h"
#include "swift/ABI/MetadataValues.h"
#include "swift/Runtime/Config.h"
#include "swift/Basic/STLExtras.h"
#include "llvm/Support/Casting.h"

namespace swift {

// ==== ------------------------------------------------------------------------
// ==== Task Options, for creating and waiting on tasks

/// The abstract base class for all options that may be used
/// to configure a newly spawned task.
class TaskOptionRecord {
public:
  const TaskOptionRecordFlags Flags;
  TaskOptionRecord *Parent;

  TaskOptionRecord(TaskOptionRecordKind kind,
                   TaskOptionRecord *parent = nullptr)
    : Flags(kind), Parent(parent) { }

  TaskOptionRecord(const TaskOptionRecord &) = delete;
  TaskOptionRecord &operator=(const TaskOptionRecord &) = delete;

  TaskOptionRecordKind getKind() const {
    return Flags.getKind();
  }

  TaskOptionRecord *getParent() const {
    return Parent;
  }
};

/******************************************************************************/
/****************************** TASK OPTIONS **********************************/
/******************************************************************************/

class TaskGroupTaskOptionRecord : public TaskOptionRecord {
  TaskGroup * const Group;

  public:
    TaskGroupTaskOptionRecord(TaskGroup *group)
    : TaskOptionRecord(TaskOptionRecordKind::TaskGroup),
      Group(group) {}

  TaskGroup *getGroup() const {
    return Group;
  }

  static bool classof(const TaskOptionRecord *record) {
    return record->getKind() == TaskOptionRecordKind::TaskGroup;
  }
};

/// Task option to specify on what executor the task should be executed.
///
/// Not passing this option (or it's alternative "owned" version) implies that
/// an inferred (e.g. surrounding actor when we inherit execution context)
/// or the default executor should be used.
///
/// Lack of this option usually means that the global concurrent executor, or
/// the executor of the enclosing actor will be used.
class InitialTaskExecutorRefPreferenceTaskOptionRecord : public TaskOptionRecord {
  const TaskExecutorRef Executor;

public:
  InitialTaskExecutorRefPreferenceTaskOptionRecord(TaskExecutorRef executor)
      : TaskOptionRecord(TaskOptionRecordKind::InitialTaskExecutorUnowned),
        Executor(executor) {}

  TaskExecutorRef getExecutorRef() const { return Executor; }

  static bool classof(const TaskOptionRecord *record) {
    return record->getKind() == TaskOptionRecordKind::InitialTaskExecutorUnowned;
  }
};

/// This is quite similar to `InitialTaskExecutorRefPreferenceTaskOptionRecord`
/// however it takes a "raw" TaskExecutor existential in the form of an Identity
/// and WitnessTable - rather than the specific UnownedTaskExecutor which already
/// may have specific "flags" set on it.
///
/// In order to use the executor in the runtime, we need to call into the type's
/// `asUnownedTaskExecutor` which is done by
/// `getExecutorRefFromUnownedTaskExecutor`.
class InitialTaskExecutorOwnedPreferenceTaskOptionRecord
    : public TaskOptionRecord {

  // These look similar to TaskExecutorRef but are NOT the same!
  // A TaskExecutorRef is obtained through calling user defined
  // `asUnownedTaskExecutor` which is what we need to do on these to get a real executor ref.
  HeapObject *Identity;
  const TaskExecutorWitnessTable *WitnessTable;

public:
  InitialTaskExecutorOwnedPreferenceTaskOptionRecord(
      HeapObject *executor, uintptr_t witnessTable)
      : TaskOptionRecord(TaskOptionRecordKind::InitialTaskExecutorOwned),
        Identity(executor) {
    WitnessTable = reinterpret_cast<const TaskExecutorWitnessTable*>(witnessTable);
  }

  /// Invokes Swift implemented `asUnownedTaskExecutor` in order to obtain an
  /// `TaskExecutorRef` which is properly populated with any flags it might need.
  TaskExecutorRef getExecutorRefFromUnownedTaskExecutor() const;

  static bool classof(const TaskOptionRecord *record) {
    return record->getKind() == TaskOptionRecordKind::InitialTaskExecutorOwned;
  }
};

class InitialTaskNameTaskOptionRecord
    : public TaskOptionRecord {

  const char* TaskName;

public:
  InitialTaskNameTaskOptionRecord(
      const char* taskName)
      : TaskOptionRecord(TaskOptionRecordKind::InitialTaskName),
        TaskName(taskName) {}

  const char* getTaskName() const {
    return TaskName;
  }

  static bool classof(const TaskOptionRecord *record) {
    return record->getKind() == TaskOptionRecordKind::InitialTaskName;
  }
};

/// Task option to specify the initial serial executor for the task.
class InitialSerialExecutorTaskOptionRecord : public TaskOptionRecord {
  const SerialExecutorRef Executor;
public:
  InitialSerialExecutorTaskOptionRecord(SerialExecutorRef executor)
      : TaskOptionRecord(TaskOptionRecordKind::InitialSerialExecutor),
        Executor(executor) {}

  SerialExecutorRef getExecutorRef() const { return Executor; }

  static bool classof(const TaskOptionRecord *record) {
    return record->getKind() == TaskOptionRecordKind::InitialSerialExecutor;
  }
};

class AsyncLetWithBufferTaskOptionRecord : public TaskOptionRecord {
  AsyncLet *asyncLet;
  void *resultBuffer;

public:
  AsyncLetWithBufferTaskOptionRecord(AsyncLet *asyncLet,
                                     void *resultBuffer)
    : TaskOptionRecord(TaskOptionRecordKind::AsyncLetWithBuffer),
      asyncLet(asyncLet),
      resultBuffer(resultBuffer) {}

  AsyncLet *getAsyncLet() const {
    return asyncLet;
  }

  void *getResultBuffer() const {
    return resultBuffer;
  }

  static bool classof(const TaskOptionRecord *record) {
    return record->getKind() == TaskOptionRecordKind::AsyncLetWithBuffer;
  }
};

#if SWIFT_CONCURRENCY_EMBEDDED
class ResultTypeInfoTaskOptionRecord : public TaskOptionRecord {
 public:
  size_t size;
  size_t alignMask;

  OpaqueValue *(*__ptrauth_swift_value_witness_function_pointer(
      SpecialPointerAuthDiscriminators::InitializeWithCopy)
            initializeWithCopy)(OpaqueValue *, OpaqueValue *, void *);

  void (*__ptrauth_swift_value_witness_function_pointer(
      SpecialPointerAuthDiscriminators::StoreEnumTagSinglePayload)
            storeEnumTagSinglePayload)(OpaqueValue *, unsigned, unsigned, void *);

  void (*__ptrauth_swift_value_witness_function_pointer(
      SpecialPointerAuthDiscriminators::Destroy) destroy)(OpaqueValue *, void *);

  static bool classof(const TaskOptionRecord *record) {
    return record->getKind() == TaskOptionRecordKind::ResultTypeInfo;
  }
};
#endif

class RunInlineTaskOptionRecord : public TaskOptionRecord {
  void *allocation;
  size_t allocationBytes;

public:
  RunInlineTaskOptionRecord(void *allocation, size_t allocationBytes)
      : TaskOptionRecord(TaskOptionRecordKind::RunInline),
        allocation(allocation), allocationBytes(allocationBytes) {}

  void *getAllocation() const { return allocation; }

  size_t getAllocationBytes() const { return allocationBytes; }

  static bool classof(const TaskOptionRecord *record) {
    return record->getKind() == TaskOptionRecordKind::RunInline;
  }
};

} // end namespace swift

#endif
