/*
 * Copyright 2009-present MongoDB, Inc.
 *
 * Licensed 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 <mongoc/mongoc-prelude.h>

#ifndef MONGOC_COUNTERS_PRIVATE_H
#define MONGOC_COUNTERS_PRIVATE_H

#include <common-atomic-private.h>

#include <mongoc/mongoc.h>

#include <bson/bson.h>

#ifdef __linux__
#include <sched.h>
#include <sys/sysinfo.h>
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
#include <sched.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#elif defined(__hpux__)
#include <sys/pstat.h>
#endif


BSON_BEGIN_DECLS


void
_mongoc_counters_init(void);
void
_mongoc_counters_cleanup(void);


static BSON_INLINE unsigned
_mongoc_get_cpu_count(void)
{
#if defined(__linux__) && defined(_SC_NPROCESSORS_CONF)
   long count = sysconf(_SC_NPROCESSORS_CONF);
   if (count < 1) {
      return 1;
   }
   return count;
#elif defined(__hpux__)
   struct pst_dynamic psd;

   if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) != -1) {
      return psd.psd_max_proc_cnt;
   }
   return 1;
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
   int mib[2];
   int maxproc;
   size_t len;

   mib[0] = CTL_HW;
   mib[1] = HW_NCPU;
   len = sizeof(maxproc);

   if (-1 == sysctl(mib, 2, &maxproc, &len, NULL, 0)) {
      return 1;
   }

   return len;
#elif defined(__APPLE__) || defined(_AIX)
   int ncpu;

   ncpu = (int)sysconf(_SC_NPROCESSORS_ONLN);
   return (ncpu > 0) ? ncpu : 1;
#elif defined(_MSC_VER) || defined(_WIN32)
   SYSTEM_INFO si;
   GetSystemInfo(&si);
   return si.dwNumberOfProcessors;
#else
#warning "_mongoc_get_cpu_count() not supported, defaulting to 1."
   return 1;
#endif
}


#if defined(MONGOC_ENABLE_RDTSCP)
static BSON_INLINE unsigned
_mongoc_sched_getcpu(void)
{
   volatile uint32_t rax, rdx, rcx;
   __asm__ volatile("rdtscp\n" : "=a"(rax), "=d"(rdx), "=c"(rcx) : :);
   unsigned core_id;
   core_id = rcx & 0xFFF;
   return core_id;
}
#elif defined(MONGOC_HAVE_SCHED_GETCPU)
#define _mongoc_sched_getcpu sched_getcpu
#elif defined(__APPLE__) && defined(__aarch64__)
static BSON_INLINE unsigned
_mongoc_sched_getcpu(void)
{
   uintptr_t tls;
   unsigned core_id;
   /* Get the current thread ID, not the core ID.
    * Getting the core ID requires privileged execution. */
   __asm__ volatile("mrs %x0, tpidrro_el0" : "=r"(tls));
   /* In ARM, only 8 cores are manageable. */
   core_id = tls & 0x07u;
   return core_id;
}
#else
#define _mongoc_sched_getcpu() (0)
#endif


#ifndef SLOTS_PER_CACHELINE
#define SLOTS_PER_CACHELINE 8
#endif


typedef struct {
   int64_t slots[SLOTS_PER_CACHELINE];
} mongoc_counter_slots_t;


typedef struct {
   mongoc_counter_slots_t *cpus;
} mongoc_counter_t;


#define COUNTER(ident, Category, Name, Description) extern mongoc_counter_t __mongoc_counter_##ident;
#include <mongoc/mongoc-counters.defs>
#undef COUNTER


enum {
#define COUNTER(ident, Category, Name, Description) COUNTER_##ident,
#include <mongoc/mongoc-counters.defs>
#undef COUNTER
   LAST_COUNTER
};

#ifdef MONGOC_ENABLE_SHM_COUNTERS
#define COUNTER(ident, Category, Name, Description)                                                                    \
   static BSON_INLINE void mongoc_counter_##ident##_add(int64_t val)                                                   \
   {                                                                                                                   \
      int64_t *counter = &BSON_CONCAT(__mongoc_counter_, ident)                                                        \
                             .cpus[_mongoc_sched_getcpu()]                                                             \
                             .slots[BSON_CONCAT(COUNTER_, ident) % SLOTS_PER_CACHELINE];                               \
      mcommon_atomic_int64_fetch_add(counter, val, mcommon_memory_order_seq_cst);                                      \
   }                                                                                                                   \
   static BSON_INLINE void mongoc_counter_##ident##_inc(void)                                                          \
   {                                                                                                                   \
      mongoc_counter_##ident##_add(1);                                                                                 \
   }                                                                                                                   \
   static BSON_INLINE void mongoc_counter_##ident##_dec(void)                                                          \
   {                                                                                                                   \
      mongoc_counter_##ident##_add(-1);                                                                                \
   }                                                                                                                   \
   static BSON_INLINE void mongoc_counter_##ident##_reset(void)                                                        \
   {                                                                                                                   \
      uint32_t i;                                                                                                      \
      for (i = 0; i < _mongoc_get_cpu_count(); i++) {                                                                  \
         int64_t *counter = &__mongoc_counter_##ident.cpus[i].slots[COUNTER_##ident % SLOTS_PER_CACHELINE];            \
         mcommon_atomic_int64_exchange(counter, 0, mcommon_memory_order_seq_cst);                                      \
      }                                                                                                                \
      mcommon_atomic_thread_fence();                                                                                   \
   }                                                                                                                   \
   static BSON_INLINE int32_t mongoc_counter_##ident##_count(void)                                                     \
   {                                                                                                                   \
      int32_t _sum = 0;                                                                                                \
      uint32_t _i;                                                                                                     \
      for (_i = 0; _i < _mongoc_get_cpu_count(); _i++) {                                                               \
         const int64_t *counter =                                                                                      \
            &BSON_CONCAT(__mongoc_counter_, ident).cpus[_i].slots[BSON_CONCAT(COUNTER_, ident) % SLOTS_PER_CACHELINE]; \
         _sum += mcommon_atomic_int64_fetch(counter, mcommon_memory_order_seq_cst);                                    \
      }                                                                                                                \
      return _sum;                                                                                                     \
   }
#include <mongoc/mongoc-counters.defs>
#undef COUNTER

#else
/* when counters are disabled, these functions are no-ops */
#define COUNTER(ident, Category, Name, Description)                  \
   static BSON_INLINE void mongoc_counter_##ident##_add(int64_t val) \
   {                                                                 \
      (void)val;                                                     \
   }                                                                 \
   static BSON_INLINE void mongoc_counter_##ident##_inc(void)        \
   {                                                                 \
   }                                                                 \
   static BSON_INLINE void mongoc_counter_##ident##_dec(void)        \
   {                                                                 \
   }                                                                 \
   static BSON_INLINE void mongoc_counter_##ident##_reset(void)      \
   {                                                                 \
   }                                                                 \
   static BSON_INLINE void mongoc_counter_##ident##_count(void)      \
   {                                                                 \
   }
#include <mongoc/mongoc-counters.defs>
#undef COUNTER
#endif

BSON_END_DECLS


#endif /* MONGOC_COUNTERS_PRIVATE_H */
