// The default operator called from parallel_for void operator() (const tbb::blocked_range<int64> &range) const { callerContext.EnteringOperator(); // Build arguments array Array args = Array::Create(); // Pass the range start and end as parameters args.append(range.begin()); args.append(range.end()); // If an input array is defined, copy it and pass as a parameter if(inputArrayOfVariant.size() > 0) { Array inputPHPArray = Array::Create(); for(size_t ai=0; ai<inputArrayOfVariant.size(); ai++) inputPHPArray.append(inputArrayOfVariant[ai]); Variant inputArrayArg(inputPHPArray.getArrayData()); args.append(inputArrayArg); } // Call user supplied callback with arguments // This is a PHP function of the form worker($begin, $end, $array), returning an array or nothing // If an array is returned, it is expected to have elements which will be comingled into the output // array in the elements defined by the input range. Variant vres = f_call_user_func_array(this->callback, args); callerContext.ExitingOperator(); // Call this straight after the callback completes // Return if no result to pass back if(vres.isNull() || !vres.isArray()) return; // Now we take the output array [0..N) and assign it into the overall output array at [begin..begin+N) // Extract output array from result variant const Array aOutputArray = vres.asCArrRef(); ArrayData *pdOutputArray = aOutputArray.getArrayData(); Variant v = pdOutputArray->reset(); // Check the output array is the same size or smaller than the range passed size_t outIdx = range.begin(); if(pdOutputArray->size() > (range.end()-range.begin())) { raise_warning("Callback function returned array larger than passed range size"); return; } // Copy each row while(!v.isBoolean() || v.toBoolean()) { // printf(" outIdx=%d, v=%s\n", (int)outIdx, v.toString().c_str()); ( *pOutputArrayOfVariant ) [outIdx++] = v; v = pdOutputArray->next(); } }
// f_parallel_for // begin - start of range to process // end - 1 after end of range to process // func - worker function of the form worker($begin, $end, $inputdata) - returns an array of max length ($end-$begin) // blocksize - optional blocksize, refer to TBB documentation // Returns: array of maximum length ($end-$begin) // // Call 'func' for non-overlapping array segments to cover all of arraydata // This will use threads as appropriate for the machine. // If a result is returned by 'func' it will be placed into the result array elements corresponding to the input elements Variant f_parallel_for_array(CVarRef arraydata, CVarRef func, int64 blocksize /* = -1 */) { if(!arraydata.isArray()) { raise_error("parallel_for_array expected an array in first argument"); return Variant(); } // Get the input array const Array aArray = arraydata.asCArrRef(); ArrayData *pdArray = aArray.getArrayData(); size_t asize = pdArray->size(); if(asize==0) return Variant(); MemoryManager *mm = MemoryManager::TheMemoryManager().get(); if (RuntimeOption::CheckMemory) { mm->checkMemory(false); } TbbContext myContext = TbbContext::Entry("parallel_for_array"); std::vector<Variant> outArray; outArray.resize(asize); // Construct task parameters PforTaskParameters tclass = PforTaskParameters(myContext, pdArray, func, &outArray); TbbInitializeIfNeeded(); if (blocksize == -1) { // Use auto_partitioner for block size tbb::parallel_for(tbb::blocked_range<int64>(0, asize), tclass, tbb::auto_partitioner()); } else { // Use manually set block size tbb::parallel_for(tbb::blocked_range<int64>(0, asize, blocksize), tclass); } myContext.Exit(); // Create the result array Array resPHPArray = Array::Create(); for(size_t ai=0; ai<outArray.size(); ai++) { Variant amem = outArray[ai]; // printf(" ai=%d, append %s\n", (int)ai, amem.toString().c_str()); if(!amem.isNull()) resPHPArray.append(amem); } if(resPHPArray.size()==0) return Variant(); // i.e null return resPHPArray.getArrayData(); }
ArrayIter getArrayIterHelper(const Variant& v, size_t& sz) { if (v.isArray()) { ArrayData* ad = v.getArrayData(); sz = ad->size(); return ArrayIter(ad); } if (v.isObject()) { ObjectData* obj = v.getObjectData(); if (obj->isCollection()) { sz = collections::getSize(obj); return ArrayIter(obj); } bool isIterable; Object iterable = obj->iterableObject(isIterable); if (isIterable) { sz = 0; return ArrayIter(iterable.detach(), ArrayIter::noInc); } } SystemLib::throwInvalidArgumentExceptionObject( "Parameter must be an array or an instance of Traversable"); }
ALWAYS_INLINE typename std::enable_if< std::is_base_of<BaseMap, TMap>::value, Object>::type BaseMap::FromArray(const Class*, const Variant& arr) { if (!arr.isArray()) { SystemLib::throwInvalidArgumentExceptionObject( "Parameter arr must be an array"); } auto map = req::make<TMap>(); ArrayData* ad = arr.getArrayData(); map->reserve(ad->size()); for (ssize_t pos = ad->iter_begin(), limit = ad->iter_end(); pos != limit; pos = ad->iter_advance(pos)) { Variant k = ad->getKey(pos); auto* tv = ad->getValueRef(pos).asCell(); if (k.isInteger()) { map->setRaw(k.toInt64(), tv); } else { assert(k.isString()); map->setRaw(k.getStringData(), tv); } } return Object(std::move(map)); }
SharedVariant::SharedVariant(CVarRef source, bool serialized, bool inner /* = false */, bool unserializeObj /* = false */) : m_count (1), m_shouldCache(false), m_flags(0){ ASSERT(!serialized || source.isString()); m_type = source.getType(); switch (m_type) { case KindOfBoolean: { m_data.num = source.toBoolean(); break; } case KindOfByte: case KindOfInt16: case KindOfInt32: case KindOfInt64: { m_type = KindOfInt64; m_data.num = source.toInt64(); break; } case KindOfDouble: { m_data.dbl = source.toDouble(); break; } case KindOfStaticString: case KindOfString: { String s = source.toString(); if (serialized) { m_type = KindOfObject; // It is priming, and there might not be the right class definitions // for unserialization. s = apc_reserialize(s); } m_data.str = s->copy(true); break; } case KindOfArray: { ArrayData *arr = source.getArrayData(); if (!inner) { // only need to call hasInternalReference() on the toplevel array PointerSet seen; if (arr->hasInternalReference(seen)) { setSerializedArray(); m_shouldCache = true; String s = apc_serialize(source); m_data.str = new StringData(s.data(), s.size(), CopyString); break; } } size_t size = arr->size(); if (arr->isVectorData()) { setIsVector(); m_data.vec = new VectorData(size); uint i = 0; for (ArrayIter it(arr); !it.end(); it.next(), i++) { SharedVariant* val = Create(it.secondRef(), false, true, unserializeObj); if (val->m_shouldCache) m_shouldCache = true; m_data.vec->vals[i] = val; } } else { m_data.map = new ImmutableMap(size); for (ArrayIter it(arr); !it.end(); it.next()) { SharedVariant* key = Create(it.first(), false, true, unserializeObj); SharedVariant* val = Create(it.secondRef(), false, true, unserializeObj); if (val->m_shouldCache) m_shouldCache = true; m_data.map->add(key, val); } } break; } case KindOfNull: { m_data.num = 0; break; } default: { ASSERT(source.isObject()); m_shouldCache = true; if (unserializeObj) { // This assumes hasInternalReference(seen, true) is false ImmutableObj* obj = new ImmutableObj(source.getObjectData()); m_data.obj = obj; setIsObj(); } else { String s = apc_serialize(source); m_data.str = new StringData(s.data(), s.size(), CopyString); } break; } } }
ThreadSharedVariant::ThreadSharedVariant(CVarRef source, bool serialized, bool inner /* = false */) { ASSERT(!serialized || source.isString()); setOwner(); m_ref = 1; switch (source.getType()) { case KindOfBoolean: { m_type = KindOfBoolean; m_data.num = source.toBoolean(); break; } case KindOfByte: case KindOfInt16: case KindOfInt32: case KindOfInt64: { m_type = KindOfInt64; m_data.num = source.toInt64(); break; } case KindOfDouble: { m_type = KindOfDouble; m_data.dbl = source.toDouble(); break; } case KindOfStaticString: case KindOfString: { String s = source.toString(); m_type = serialized ? KindOfObject : KindOfString; if (serialized) { // It is priming, and there might not be the right class definitions // for unserialization. s = apc_reserialize(s); } m_data.str = s->copy(true); break; } case KindOfArray: { m_type = KindOfArray; ArrayData *arr = source.getArrayData(); if (!inner) { // only need to call hasInternalReference() on the toplevel array PointerSet seen; if (arr->hasInternalReference(seen)) { setSerializedArray(); setShouldCache(); String s = apc_serialize(source); m_data.str = new StringData(s.data(), s.size(), CopyString); break; } } size_t size = arr->size(); if (arr->isVectorData()) { setIsVector(); m_data.vec = new VectorData(size); uint i = 0; for (ArrayIter it(arr); !it.end(); it.next(), i++) { ThreadSharedVariant* val = createAnother(it.second(), false, true); if (val->shouldCache()) setShouldCache(); m_data.vec->vals[i] = val; } } else { m_data.map = new ImmutableMap(size); uint i = 0; for (ArrayIter it(arr); !it.end(); it.next(), i++) { ThreadSharedVariant* key = createAnother(it.first(), false); ThreadSharedVariant* val = createAnother(it.second(), false, true); if (val->shouldCache()) setShouldCache(); m_data.map->add(key, val); } } break; } default: { m_type = KindOfObject; setShouldCache(); String s = apc_serialize(source); m_data.str = new StringData(s.data(), s.size(), CopyString); break; } } }
SharedVariant::SharedVariant(CVarRef source, bool serialized, bool inner /* = false */, bool unserializeObj /* = false */) : m_shouldCache(false), m_flags(0) { assert(!serialized || source.isString()); m_count = 1; m_type = source.getType(); switch (m_type) { case KindOfBoolean: { m_data.num = source.toBoolean(); break; } case KindOfInt64: { m_type = KindOfInt64; m_data.num = source.toInt64(); break; } case KindOfDouble: { m_data.dbl = source.toDouble(); break; } case KindOfStaticString: { if (serialized) goto StringCase; m_data.str = source.getStringData(); break; } StringCase: case KindOfString: { String s = source.toString(); if (serialized) { m_type = KindOfObject; // It is priming, and there might not be the right class definitions // for unserialization. s = apc_reserialize(s); } StringData* st = StringData::LookupStaticString(s.get()); if (st) { m_data.str = st; m_type = KindOfStaticString; break; } m_data.str = s->copy(true); break; } case KindOfArray: { ArrayData *arr = source.getArrayData(); if (!inner) { // only need to call hasInternalReference() on the toplevel array PointerSet seen; if (arr->hasInternalReference(seen)) { setSerializedArray(); m_shouldCache = true; String s = apc_serialize(source); m_data.str = StringData::MakeMalloced(s.data(), s.size()); break; } } if (arr->isVectorData()) { setIsVector(); m_data.vec = new (arr->size()) VectorData(); for (ArrayIter it(arr); !it.end(); it.next()) { SharedVariant* val = Create(it.secondRef(), false, true, unserializeObj); if (val->m_shouldCache) m_shouldCache = true; m_data.vec->vals()[m_data.vec->m_size++] = val; } } else { m_data.map = ImmutableMap::Create(arr, unserializeObj, m_shouldCache); } break; } case KindOfUninit: case KindOfNull: { break; } case KindOfResource: { // TODO Task #2661075: Here and elsewhere in the runtime, we convert // Resources to the empty array during various serialization operations, // which does not match Zend behavior. We should fix this. m_type = KindOfArray; setIsVector(); m_data.vec = new (0) VectorData(); break; } default: { assert(source.isObject()); m_shouldCache = true; if (unserializeObj) { // This assumes hasInternalReference(seen, true) is false ImmutableObj* obj = new ImmutableObj(source.getObjectData()); m_data.obj = obj; setIsObj(); } else { String s = apc_serialize(source); m_data.str = StringData::MakeMalloced(s.data(), s.size()); } break; } } assert(m_type != KindOfResource); }